<commit_before>/*
 * Copyright (C) 2008  Barracuda Networks, Inc.
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
 * version 2.1 of the License, or (at your option) any later version.
 *
 * This library is distributed in the hope that it will be useful,
 * but WITHANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with this library; if not, write to the Free Software
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
 * 02110-1301  USA
 *
 */

#include "devices.h"

#include <QSize>
#include <QStringList>
#include <gst/gst.h>
#include <gst/interfaces/propertyprobe.h>
#include "deviceenum/deviceenum.h"

namespace PsiMedia {

class GstDeviceProbeValue
{
public:
	QString id;
	QString name;
};

static QList<GstDeviceProbeValue> device_probe(GstElement *e)
{
	GObjectClass *klass = G_OBJECT_GET_CLASS(e);
	if(!g_object_class_find_property(klass, "device") || !GST_IS_PROPERTY_PROBE(e))
		return QList<GstDeviceProbeValue>();

	GstPropertyProbe *probe = GST_PROPERTY_PROBE(e);
	if(!probe)
		return QList<GstDeviceProbeValue>();

	const GParamSpec *pspec = gst_property_probe_get_property(probe, "device");
	if(!pspec)
		return QList<GstDeviceProbeValue>();

	QList<GstDeviceProbeValue> out;

	GValueArray *list = gst_property_probe_probe_and_get_values(probe, pspec);
	if(list)
	{
		for(int n = 0; n < (int)list->n_values; ++n)
		{
			GValue *i = g_value_array_get_nth(list, n);

			// FIXME: "device" isn't always a string
			gchar *name;
			g_object_set(G_OBJECT(e), "device", g_value_get_string(i), NULL);
			g_object_get(G_OBJECT(e), "device-name", &name, NULL);

			GstDeviceProbeValue dev;
			dev.id = QString::fromUtf8(g_value_get_string(i));
			dev.name = QString::fromUtf8(name);
			g_free(name);

			out += dev;
		}

		g_value_array_free(list);
	}

	return out;
}

static bool element_should_use_probe(const QString &element_name)
{
	// we can enumerate devices in two ways.  one is via gst propery
	//   probing and the other is through our own DeviceEnum code.
	//   since gst property probing is "the future", we'll take a
	//   probe-by-default approach, and only use DeviceEnum for specific
	//   elements

	// these should use DeviceEnum
	if(element_name == "alsasrc" ||
		element_name == "alsasink" ||
		element_name == "v4lsrc" ||
		element_name == "v4l2src" ||
		element_name == "osxaudiosrc" ||
		element_name == "osxaudiosink" ||
		element_name == "directsoundsrc" ||
		element_name == "directsoundsink" ||
		element_name == "ksvideosrc")
	{
		return false;
	}
	// all else probe
	else
		return true;
}

static QList<DeviceEnum::Item> device_enum(const QString &driver, PDevice::Type type)
{
	if(type == PDevice::AudioOut)
		return DeviceEnum::audioOutputItems(driver);
	else if(type == PDevice::AudioIn)
		return DeviceEnum::audioInputItems(driver);
	else // PDevice::VideoIn
		return DeviceEnum::videoInputItems(driver);
}

static QString id_part_escape(const QString &in)
{
	QString out;
	for(int n = 0; n < in.length(); ++n)
	{
		if(in[n] == '\\')
			out += "\\\\";
		else if(in[n] == ',')
			out += "\\c";
		else
			out += in[n];
	}
	return out;
}

static QString id_part_unescape(const QString &in)
{
	QString out;
	for(int n = 0; n < in.length(); ++n)
	{
		if(in[n] == '\\')
		{
			if(n + 1 >= in.length())
				return QString();

			++n;
			if(in[n] == '\\')
				out += '\\';
			else if(in[n] == 'c')
				out += ',';
			else
				return QString();
		}
		else
			out += in[n];
	}
	return out;
}

static QString resolution_to_string(const QSize &size)
{
	return QString::number(size.width()) + 'x' + QString::number(size.height());
}

static QSize string_to_resolution(const QString &in)
{
	int at = in.indexOf('x');
	if(at == -1)
		return QSize();

	QString ws = in.mid(0, at);
	QString hs = in.mid(at + 1);

	bool ok;
	int w = ws.toInt(&ok);
	if(!ok)
		return QSize();

	int h = hs.toInt(&ok);
	if(!ok)
		return QSize();

	return QSize(w, h);
}

static QString encode_id(const QStringList &in)
{
	QStringList list = in;
	for(int n = 0; n < list.count(); ++n)
		list[n] = id_part_escape(list[n]);
	return list.join(",");
}

static QStringList decode_id(const QString &in)
{
	QStringList list = in.split(',');
	for(int n = 0; n < list.count(); ++n)
		list[n] = id_part_unescape(list[n]);
	return list;
}

static QString element_name_for_driver(const QString &driver, PDevice::Type type)
{
	QString element_name;

	if(driver == "alsa")
	{
		if(type == PDevice::AudioOut)
			element_name = "alsasink";
		else if(type == PDevice::AudioIn)
			element_name = "alsasrc";
	}
	else if(driver == "osxaudio")
	{
		if(type == PDevice::AudioOut)
			element_name = "osxaudiosink";
		else if(type == PDevice::AudioIn)
			element_name = "osxaudiosrc";
	}
	else if(driver == "osxvideo")
	{
		if(type == PDevice::VideoIn)
			element_name = "osxvideosrc";
	}
	else if(driver == "v4l")
	{
		if(type == PDevice::VideoIn)
			element_name = "v4lsrc";
	}
	else if(driver == "v4l2")
	{
		if(type == PDevice::VideoIn)
			element_name = "v4l2src";
	}
	else if(driver == "directsound")
	{
		if(type == PDevice::AudioOut)
			element_name = "directsoundsink";
		else if(type == PDevice::AudioIn)
			element_name = "directsoundsrc";
	}
	else if(driver == "winks")
	{
		if(type == PDevice::VideoIn)
			element_name = "ksvideosrc";
	}

	return element_name;
}

// check to see that the necessary sources/sinks are available
static QStringList check_supported_drivers(const QStringList &drivers, PDevice::Type type)
{
	QStringList out;
	foreach(const QString &driver, drivers)
	{
		QString element_name = element_name_for_driver(driver, type);
		if(element_name.isEmpty())
			continue;

		GstElement *e = gst_element_factory_make(element_name.toLatin1().data(), NULL);
		if(e)
		{
			out += driver;
			g_object_unref(G_OBJECT(e));
		}
	}
	return out;
}

static GstElement *make_element_with_device(const QString &element_name, const QString &device_id)
{
	GstElement *e = gst_element_factory_make(element_name.toLatin1().data(), NULL);
	if(!e)
		return 0;

	if(!device_id.isEmpty())
	{
		// FIXME: is there a better way to determine if "device" is a string or int?
		if(element_name == "osxaudiosrc" || element_name == "osxaudiosink")
			g_object_set(G_OBJECT(e), "device", device_id.toInt(), NULL);
		else
			g_object_set(G_OBJECT(e), "device", device_id.toLatin1().data(), NULL);
	}

	return e;
}

static bool test_video(const QString &element_name, const QString &device_id)
{
	GstElement *e = make_element_with_device(element_name, device_id);
	if(!e)
		return false;

	gst_element_set_state(e, GST_STATE_PAUSED);
	int ret = gst_element_get_state(e, NULL, NULL, GST_CLOCK_TIME_NONE);

	// 'ret' has our answer, so we can free up the element now
	gst_element_set_state(e, GST_STATE_NULL);
	gst_element_get_state(e, NULL, NULL, GST_CLOCK_TIME_NONE);
	g_object_unref(G_OBJECT(e));

	if(ret != GST_STATE_CHANGE_SUCCESS && ret != GST_STATE_CHANGE_NO_PREROLL)
		return false;

	return true;
}

// for elements that we can't enumerate devices for, we need a way to ensure
//   that at least the default device works
// FIXME: why do we have both this function and test_video() ?
static bool test_element(const QString &element_name)
{
	GstElement *e = gst_element_factory_make(element_name.toLatin1().data(), NULL);
	if(!e)
		return 0;

	gst_element_set_state(e, GST_STATE_READY);
	int ret = gst_element_get_state(e, NULL, NULL, GST_CLOCK_TIME_NONE);

	gst_element_set_state(e, GST_STATE_NULL);
	gst_element_get_state(e, NULL, NULL, GST_CLOCK_TIME_NONE);
	g_object_unref(G_OBJECT(e));

	if(ret != GST_STATE_CHANGE_SUCCESS)
		return false;

	return true;
}

static QList<GstDevice> devices_for_drivers(const QStringList &drivers, PDevice::Type type)
{
	QList<GstDevice> out;

	QStringList supportedDrivers = check_supported_drivers(drivers, type);
	foreach(const QString &driver, supportedDrivers)
	{
		QString element_name = element_name_for_driver(driver, type);
		if(element_should_use_probe(element_name))
		{
			GstElement *e = gst_element_factory_make(element_name.toLatin1().data(), NULL);
			QList<GstDeviceProbeValue> list = device_probe(e);
			g_object_unref(G_OBJECT(e));

			bool first = true;
			foreach(const GstDeviceProbeValue &i, list)
			{
				GstDevice dev;
				dev.name = i.name + QString(" (%1)").arg(driver);
				dev.isDefault = first;

				QStringList parts;
				parts += driver;
				parts += i.id;
				dev.id = encode_id(parts);

				out += dev;
				first = false;
			}
		}
		else
		{
			QList<DeviceEnum::Item> list = device_enum(driver, type);

			bool first = true;
			foreach(const DeviceEnum::Item &i, list)
			{
				if(type == PDevice::VideoIn && (element_name == "v4lsrc" || element_name == "v4l2src"))
				{
					if(!test_video(element_name, i.id))
						continue;
				}
				else if(element_name == "directsoundsrc" || element_name == "directsoundsink" || element_name == "ksvideosrc" || element_name == "osxvideosrc")
				{
					if(!test_element(element_name))
						continue;
				}

				GstDevice dev;
				dev.name = i.name + QString(" (%1)").arg(i.driver);
				dev.isDefault = first;

				QStringList parts;
				parts += i.driver;
				parts += i.id;
				if(!i.explicitCaptureSize.isNull())
					parts += resolution_to_string(i.explicitCaptureSize);
				dev.id = encode_id(parts);

				out += dev;
				first = false;
			}
		}
	}

	return out;
}

QList<GstDevice> devices_list(PDevice::Type type)
{
	QStringList drivers;
	if(type == PDevice::AudioOut)
	{
		drivers
		<< "alsa"
		<< "osxaudio"
		<< "directsound";
	}
	else if(type == PDevice::AudioIn)
	{
		drivers
		<< "alsa"
		<< "osxaudio"
		<< "directsound";
	}
	else // PDevice::VideoIn
	{
		drivers
		<< "v4l"
		<< "v4l2"
		<< "osxvideo"
		<< "winks";
	}

	return devices_for_drivers(drivers, type);
}

GstElement *devices_makeElement(const QString &id, PDevice::Type type, QSize *captureSize)
{
	QStringList parts = decode_id(id);
	if(parts.count() < 2)
		return 0;

	QString driver = parts[0];
	QString device_id = parts[1];
	QString element_name = element_name_for_driver(driver, type);
	if(element_name.isEmpty())
		return 0;

	GstElement *e = make_element_with_device(element_name, device_id);
	if(!e)
		return 0;

	gst_element_set_state(e, GST_STATE_READY);
	int ret = gst_element_get_state(e, NULL, NULL, GST_CLOCK_TIME_NONE);
	if(ret != GST_STATE_CHANGE_SUCCESS)
	{
		g_object_unref(G_OBJECT(e));
		return 0;
	}

	if(parts.count() >= 3 && captureSize)
		*captureSize = string_to_resolution(parts[2]);

	return e;
}

}
<commit_msg>temporary workaround for v4l2src...<commit_after>/*
 * Copyright (C) 2008  Barracuda Networks, Inc.
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
 * version 2.1 of the License, or (at your option) any later version.
 *
 * This library is distributed in the hope that it will be useful,
 * but WITHANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with this library; if not, write to the Free Software
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
 * 02110-1301  USA
 *
 */

#include "devices.h"

#include <QSize>
#include <QStringList>
#include <gst/gst.h>
#include <gst/interfaces/propertyprobe.h>
#include "deviceenum/deviceenum.h"

namespace PsiMedia {

class GstDeviceProbeValue
{
public:
	QString id;
	QString name;
};

static QList<GstDeviceProbeValue> device_probe(GstElement *e)
{
	GObjectClass *klass = G_OBJECT_GET_CLASS(e);
	if(!g_object_class_find_property(klass, "device") || !GST_IS_PROPERTY_PROBE(e))
		return QList<GstDeviceProbeValue>();

	GstPropertyProbe *probe = GST_PROPERTY_PROBE(e);
	if(!probe)
		return QList<GstDeviceProbeValue>();

	const GParamSpec *pspec = gst_property_probe_get_property(probe, "device");
	if(!pspec)
		return QList<GstDeviceProbeValue>();

	QList<GstDeviceProbeValue> out;

	GValueArray *list = gst_property_probe_probe_and_get_values(probe, pspec);
	if(list)
	{
		for(int n = 0; n < (int)list->n_values; ++n)
		{
			GValue *i = g_value_array_get_nth(list, n);

			// FIXME: "device" isn't always a string
			gchar *name;
			g_object_set(G_OBJECT(e), "device", g_value_get_string(i), NULL);
			g_object_get(G_OBJECT(e), "device-name", &name, NULL);

			GstDeviceProbeValue dev;
			dev.id = QString::fromUtf8(g_value_get_string(i));
			dev.name = QString::fromUtf8(name);
			g_free(name);

			out += dev;
		}

		g_value_array_free(list);
	}

	return out;
}

static bool element_should_use_probe(const QString &element_name)
{
	// we can enumerate devices in two ways.  one is via gst propery
	//   probing and the other is through our own DeviceEnum code.
	//   since gst property probing is "the future", we'll take a
	//   probe-by-default approach, and only use DeviceEnum for specific
	//   elements

	// these should use DeviceEnum
	if(element_name == "alsasrc" ||
		element_name == "alsasink" ||
		element_name == "v4lsrc" ||
		element_name == "v4l2src" ||
		element_name == "osxaudiosrc" ||
		element_name == "osxaudiosink" ||
		element_name == "directsoundsrc" ||
		element_name == "directsoundsink" ||
		element_name == "ksvideosrc")
	{
		return false;
	}
	// all else probe
	else
		return true;
}

static QList<DeviceEnum::Item> device_enum(const QString &driver, PDevice::Type type)
{
	if(type == PDevice::AudioOut)
		return DeviceEnum::audioOutputItems(driver);
	else if(type == PDevice::AudioIn)
		return DeviceEnum::audioInputItems(driver);
	else // PDevice::VideoIn
		return DeviceEnum::videoInputItems(driver);
}

static QString id_part_escape(const QString &in)
{
	QString out;
	for(int n = 0; n < in.length(); ++n)
	{
		if(in[n] == '\\')
			out += "\\\\";
		else if(in[n] == ',')
			out += "\\c";
		else
			out += in[n];
	}
	return out;
}

static QString id_part_unescape(const QString &in)
{
	QString out;
	for(int n = 0; n < in.length(); ++n)
	{
		if(in[n] == '\\')
		{
			if(n + 1 >= in.length())
				return QString();

			++n;
			if(in[n] == '\\')
				out += '\\';
			else if(in[n] == 'c')
				out += ',';
			else
				return QString();
		}
		else
			out += in[n];
	}
	return out;
}

static QString resolution_to_string(const QSize &size)
{
	return QString::number(size.width()) + 'x' + QString::number(size.height());
}

static QSize string_to_resolution(const QString &in)
{
	int at = in.indexOf('x');
	if(at == -1)
		return QSize();

	QString ws = in.mid(0, at);
	QString hs = in.mid(at + 1);

	bool ok;
	int w = ws.toInt(&ok);
	if(!ok)
		return QSize();

	int h = hs.toInt(&ok);
	if(!ok)
		return QSize();

	return QSize(w, h);
}

static QString encode_id(const QStringList &in)
{
	QStringList list = in;
	for(int n = 0; n < list.count(); ++n)
		list[n] = id_part_escape(list[n]);
	return list.join(",");
}

static QStringList decode_id(const QString &in)
{
	QStringList list = in.split(',');
	for(int n = 0; n < list.count(); ++n)
		list[n] = id_part_unescape(list[n]);
	return list;
}

static QString element_name_for_driver(const QString &driver, PDevice::Type type)
{
	QString element_name;

	if(driver == "alsa")
	{
		if(type == PDevice::AudioOut)
			element_name = "alsasink";
		else if(type == PDevice::AudioIn)
			element_name = "alsasrc";
	}
	else if(driver == "osxaudio")
	{
		if(type == PDevice::AudioOut)
			element_name = "osxaudiosink";
		else if(type == PDevice::AudioIn)
			element_name = "osxaudiosrc";
	}
	else if(driver == "osxvideo")
	{
		if(type == PDevice::VideoIn)
			element_name = "osxvideosrc";
	}
	else if(driver == "v4l")
	{
		if(type == PDevice::VideoIn)
			element_name = "v4lsrc";
	}
	else if(driver == "v4l2")
	{
		if(type == PDevice::VideoIn)
			element_name = "v4l2src";
	}
	else if(driver == "directsound")
	{
		if(type == PDevice::AudioOut)
			element_name = "directsoundsink";
		else if(type == PDevice::AudioIn)
			element_name = "directsoundsrc";
	}
	else if(driver == "winks")
	{
		if(type == PDevice::VideoIn)
			element_name = "ksvideosrc";
	}

	return element_name;
}

// check to see that the necessary sources/sinks are available
static QStringList check_supported_drivers(const QStringList &drivers, PDevice::Type type)
{
	QStringList out;
	foreach(const QString &driver, drivers)
	{
		QString element_name = element_name_for_driver(driver, type);
		if(element_name.isEmpty())
			continue;

		GstElement *e = gst_element_factory_make(element_name.toLatin1().data(), NULL);
		if(e)
		{
			out += driver;
			g_object_unref(G_OBJECT(e));
		}
	}
	return out;
}

static GstElement *make_element_with_device(const QString &element_name, const QString &device_id)
{
	GstElement *e = gst_element_factory_make(element_name.toLatin1().data(), NULL);
	if(!e)
		return 0;

	if(!device_id.isEmpty())
	{
		// FIXME: is there a better way to determine if "device" is a string or int?
		if(element_name == "osxaudiosrc" || element_name == "osxaudiosink")
			g_object_set(G_OBJECT(e), "device", device_id.toInt(), NULL);
		else
			g_object_set(G_OBJECT(e), "device", device_id.toLatin1().data(), NULL);
	}

	return e;
}

static bool test_video(const QString &element_name, const QString &device_id)
{
	GstElement *e = make_element_with_device(element_name, device_id);
	if(!e)
		return false;

	gst_element_set_state(e, GST_STATE_PAUSED);
	int ret = gst_element_get_state(e, NULL, NULL, GST_CLOCK_TIME_NONE);

	// 'ret' has our answer, so we can free up the element now
	gst_element_set_state(e, GST_STATE_NULL);
	gst_element_get_state(e, NULL, NULL, GST_CLOCK_TIME_NONE);
	g_object_unref(G_OBJECT(e));

	if(ret != GST_STATE_CHANGE_SUCCESS && ret != GST_STATE_CHANGE_NO_PREROLL)
		return false;

	return true;
}

// for elements that we can't enumerate devices for, we need a way to ensure
//   that at least the default device works
// FIXME: why do we have both this function and test_video() ?
static bool test_element(const QString &element_name)
{
	GstElement *e = gst_element_factory_make(element_name.toLatin1().data(), NULL);
	if(!e)
		return 0;

	gst_element_set_state(e, GST_STATE_READY);
	int ret = gst_element_get_state(e, NULL, NULL, GST_CLOCK_TIME_NONE);

	gst_element_set_state(e, GST_STATE_NULL);
	gst_element_get_state(e, NULL, NULL, GST_CLOCK_TIME_NONE);
	g_object_unref(G_OBJECT(e));

	if(ret != GST_STATE_CHANGE_SUCCESS)
		return false;

	return true;
}

static QList<GstDevice> devices_for_drivers(const QStringList &drivers, PDevice::Type type)
{
	QList<GstDevice> out;

	QStringList supportedDrivers = check_supported_drivers(drivers, type);
	foreach(const QString &driver, supportedDrivers)
	{
		QString element_name = element_name_for_driver(driver, type);
		if(element_should_use_probe(element_name))
		{
			GstElement *e = gst_element_factory_make(element_name.toLatin1().data(), NULL);
			QList<GstDeviceProbeValue> list = device_probe(e);
			g_object_unref(G_OBJECT(e));

			bool first = true;
			foreach(const GstDeviceProbeValue &i, list)
			{
				GstDevice dev;
				dev.name = i.name + QString(" (%1)").arg(driver);
				dev.isDefault = first;

				QStringList parts;
				parts += driver;
				parts += i.id;
				dev.id = encode_id(parts);

				out += dev;
				first = false;
			}
		}
		else
		{
			QList<DeviceEnum::Item> list = device_enum(driver, type);

			bool first = true;
			foreach(const DeviceEnum::Item &i, list)
			{
				if(type == PDevice::VideoIn && (element_name == "v4lsrc" || element_name == "v4l2src"))
				{
					if(!test_video(element_name, i.id))
						continue;
				}
				else if(element_name == "directsoundsrc" || element_name == "directsoundsink" || element_name == "ksvideosrc" || element_name == "osxvideosrc")
				{
					if(!test_element(element_name))
						continue;
				}

				GstDevice dev;
				dev.name = i.name + QString(" (%1)").arg(i.driver);
				dev.isDefault = first;

				QStringList parts;
				parts += i.driver;
				parts += i.id;
				if(!i.explicitCaptureSize.isNull())
					parts += resolution_to_string(i.explicitCaptureSize);
				dev.id = encode_id(parts);

				out += dev;
				first = false;
			}
		}
	}

	return out;
}

QList<GstDevice> devices_list(PDevice::Type type)
{
	QStringList drivers;
	if(type == PDevice::AudioOut)
	{
		drivers
		<< "alsa"
		<< "osxaudio"
		<< "directsound";
	}
	else if(type == PDevice::AudioIn)
	{
		drivers
		<< "alsa"
		<< "osxaudio"
		<< "directsound";
	}
	else // PDevice::VideoIn
	{
		drivers
		<< "v4l"
		<< "v4l2"
		<< "osxvideo"
		<< "winks";
	}

	return devices_for_drivers(drivers, type);
}

GstElement *devices_makeElement(const QString &id, PDevice::Type type, QSize *captureSize)
{
	QStringList parts = decode_id(id);
	if(parts.count() < 2)
		return 0;

	QString driver = parts[0];
	QString device_id = parts[1];
	QString element_name = element_name_for_driver(driver, type);
	if(element_name.isEmpty())
		return 0;

	GstElement *e = make_element_with_device(element_name, device_id);
	if(!e)
		return 0;

	// FIXME: we don't set v4l2src to the READY state because it may break
	//   the element in jpeg mode.  this is really a bug in gstreamer or
	//   lower that should be fixed...
	if(element_name != "v4l2src")
	{
		gst_element_set_state(e, GST_STATE_READY);
		int ret = gst_element_get_state(e, NULL, NULL, GST_CLOCK_TIME_NONE);
		if(ret != GST_STATE_CHANGE_SUCCESS)
		{
			g_object_unref(G_OBJECT(e));
			return 0;
		}
	}

	if(parts.count() >= 3 && captureSize)
		*captureSize = string_to_resolution(parts[2]);

	return e;
}

}
<|endoftext|>


<commit_before>#ifndef OCTARINE_LANG
#define OCTARINE_LANG

// Table of contents
// ## 00 Macro Definitions
// ## 10 Includes
// ## 20 Type declarations and typedefs
// ## 30 Function declarations
// ## 40 Type definitions
// ## 50 Function definitions
// ## 60 C API

// ## 00 Macro Definitions

#ifdef _WIN32
#define WINDOWS

#ifdef _DEBUG
#define DEBUG
#endif

#ifdef _WIN64
#define OCT64
#else
#define OCT32
#endif

#elif defined (__APPLE__)
#define MACOSX

#ifdef __LP64__
#define OCT64
#else
#define OCT32
#endif

#else
#define LINUX

#ifndef NDEBUG
#define DEBUG
#endif

#ifdef __LP64__
#define OCT64
#else
#define OCT32
#endif

#endif

#define True 1
#define False 0

// ## 10 Includes

#ifdef WINDOWS
#include <Windows.h>
#else
#include <inttypes.h>
#include <pthread.h>
#endif
#include <unordered_map>
#include <unordered_set>
#include <vector>
#include <algorithm>
#include <mutex>

namespace octarine {

	// ## 20 Type declarations and typedefs

#ifdef WINDOWS
	typedef __int8           I8;
	typedef unsigned __int8  U8;
	typedef __int16          I16;
	typedef unsigned __int16 U16;
	typedef __int32          I32;
	typedef unsigned __int32 U32;
	typedef __int64          I64;
	typedef unsigned __int64 U64;
#else
	typedef int8_t   I8;
	typedef uint8_t  U8;
	typedef int16_t  I16;
	typedef uint16_t U16;
	typedef int32_t  I32;
	typedef uint32_t U32;
	typedef int64_t  I64;
	typedef uint64_t U64;
#endif
	typedef float    F32;
	typedef double   F64;
	typedef U8 Bool;
	typedef I32 Char;
#ifdef OCT64
	typedef I64 Word;
	typedef U64 Uword;
#else
	typedef I32 Word;
	typedef U32 Uword;
#endif
	typedef void* Address;

	struct FunctionT;
	typedef FunctionT* Function;

	struct MemoryManagerT;
	typedef MemoryManagerT* MemoryManager;

	struct NamespaceT;
	typedef NamespaceT* Namespace;

	struct ObjectT;
	typedef ObjectT* Object;

	struct ReaderT;
	typedef ReaderT* Reader;

	struct RuntimeT;
	typedef RuntimeT* Runtime;

	struct StringT;
	typedef StringT* String;

	struct ThreadContextT;
	typedef ThreadContextT* ThreadContext;

	struct TypeT;
	typedef TypeT* Type;

	struct ExceptionT;
	typedef ExceptionT* Exception;

	// ## 30 Function declarations

	static MemoryManager createMemoryManager();

	static void destroyMemoryManager(MemoryManager mm);

	static Object allocRaw(MemoryManager mm, Uword size);

	static Object alloc(ThreadContext tc, MemoryManager mm, Type t);

	static Namespace createNamespace(ThreadContext tc, String name);

	static Object bind(ThreadContext tc, Namespace ns, String name, Object obj);

	static Type getNamespaceType(Runtime rt);

	static Bool equals(ThreadContext tc, Namespace ns, Object obj);

	static Type getType(ThreadContext tc, Object obj);

	static Uword hash(ThreadContext tc, Object o);

	static Bool equals(ThreadContext tc, Object o1, Object o2);

	static String makeShared(ThreadContext tc, String s);

	static Namespace makeShared(ThreadContext tc, Namespace s);

	static Runtime createRuntime();

	static void destroyRuntime(Runtime rt);

	static void registerFunction(ThreadContext tc, Namespace ns, Function f);

	static Object eval(ThreadContext tc, String source);

	static Object intern(ThreadContext tc, Object o);

	static String createString(ThreadContext tc, const char* cstr);

	static Uword hash(ThreadContext tc, String s);

	static Bool equals(ThreadContext tc, String s1, String s2);

	static ThreadContext createThreadContext(Runtime rt, MemoryManager mm, Namespace initialNs);

	static void destroyThreadContext(ThreadContext tc);

	static ThreadContext getThreadContext();

	static Runtime getRuntime(ThreadContext tc);

	static MemoryManager getMemoryManager(ThreadContext tc);

	static Namespace getNamespace(ThreadContext tc);

	static Namespace setNamespace(ThreadContext tc, Namespace ns);

	static Exception createException(ThreadContext tc, String message);

	static String getMessage(ThreadContext tc, Exception e);

	// ## 40 Type definitions

	// STL Integration
}
namespace std {
	template <>
	struct hash<octarine::String> {
		size_t operator()(const octarine::String& s) const {
			return octarine::hash(octarine::getThreadContext(), s);
		}
	};

	template<>
	struct equal_to<octarine::String> {
		bool operator()(const octarine::String& s1, const octarine::String& s2) const {
			return octarine::equals(octarine::getThreadContext(), s1, s2) == True ? true : false;
		}
	};

	template<>
	struct hash<octarine::Object> {
		size_t operator()(const octarine::Object& o) const {
			return octarine::hash(octarine::getThreadContext(), o);
		}
	};

	template<>
	struct equal_to<octarine::Object> {
		bool operator()(const octarine::Object& o1, const octarine::Object& o2) {
			return octarine::equals(octarine::getThreadContext(), o1, o2) == True ? true : false;
		}
	};
}
namespace octarine {

	// Platform support

	class TLS {
	private:
#ifdef WINDOWS
		DWORD threadLocalStorage;
#elif defined (MACOSX)
		pthread_key_t threadLocalStorage;
#else
#endif
	public:
		TLS() {
#ifdef WINDOWS
			threadLocalStorage = TlsAlloc();
#elif defined (MACOSX)
			pthread_key_create(&threadLocalStorage, nullptr);
#else
#endif
		}

		~TLS() {
#ifdef WINDOWS
			TlsFree(threadLocalStorage);
#elif defined (MACOSX)
			pthread_key_delete(threadLocalStorage);
#else
#endif
		}

		void set(void* value) {
#ifdef WINDOWS
			TlsSetValue(threadLocalStorage, value);
#elif defined (MACOSX)
			pthread_setspecific(threadLocalStorage, value);
#else
#endif
		}

		void* get() {
#ifdef WINDOWS
			return TlsGetValue(threadLocalStorage);
#elif defined (MACOSX)
			return pthread_getspecific(threadLocalStorage);
#else
#endif
		}
	};

	// Octarine types

	struct MemoryManagerT {
		Uword hello;
	};

	struct NamespaceT {
		String name;
	};

	struct StringT {
		Uword length;
		U8* utf8data; // Just use platform strings?
	};

	struct BuiltInTypes {
		Type namespaceType;
	};

	struct ThreadContextT {
		Runtime runtime;
		MemoryManager memoryManager;
		Namespace currentNs;
	};

	struct RuntimeT {
		std::mutex namespaceMutex;
		std::unordered_map<String, Namespace> namespaces;
		std::mutex threadContextMutex;
		std::vector<ThreadContext> threadContexts;
		std::mutex internedObjectsMutex;
		std::unordered_set<Object> internedObjects;
		BuiltInTypes builtInTypes;
	};

    // ## 50 Function definitions

	// Global thread local var for getting at the current thread context when it is impossible
	// to pass it along. For example when integrating with the STL or in callbacks from C code.
	static TLS tls;

	static MemoryManager createMemoryManager() {
		MemoryManager mm = new MemoryManagerT;
		return mm;
	}

	static void destroyMemoryManager(MemoryManager mm) {
		delete mm;
	}

	static Object allocRaw(MemoryManager mm, Uword size) {
		// TODO: allocate space for Object header as well and return pointer
		// to memory after the header.
		return (Object)::operator new(size);
	}

	static Object alloc(ThreadContext tc, MemoryManager mm, Type t) {
		return nullptr;
	}

	static Namespace createNamespace(ThreadContext tc, String name) {
		MemoryManager mm = getMemoryManager(tc);
		Runtime rt = getRuntime(tc);
		Namespace ns = (Namespace) allocRaw(mm, sizeof(NamespaceT));
		ns->name = name;
		rt->namespaces[name] = ns;
		return makeShared(tc, ns);
	}

	static Object bind(ThreadContext tc, Namespace ns, String name, Object obj) {

	}

	static Type getNamespaceType(Runtime rt) {
		return rt->builtInTypes.namespaceType;
	}

	static Bool equals(ThreadContext tc, Namespace ns, Object obj) {

	}

	static Type getType(ThreadContext tc, Object obj) {

	}

	static Uword hash(ThreadContext tc, Object o) {

	}

	static Bool equals(ThreadContext tc, Object o1, Object o2) {

	}

	static String makeShared(ThreadContext tc, String s) {

	}

	static Namespace makeShared(ThreadContext tc, Namespace s) {
		return nullptr;
	}

	static Runtime createRuntime() {
		Runtime rt = new RuntimeT;
		// Create main thread context, with its own memory manager
		ThreadContext mainTc = createThreadContext(rt, createMemoryManager(), nullptr);
		rt->threadContexts.push_back(mainTc);
		// Create main namespace
		setNamespace(mainTc, createNamespace(mainTc, createString(mainTc, "octarine")));
		return rt;
	}

	static void destroyRuntime(Runtime rt) {
		std::for_each(rt->threadContexts.begin(), rt->threadContexts.end(), destroyThreadContext);
		delete rt;
	}

	static void registerFunction(ThreadContext tc, Namespace ns, Function f) {

	}

	static Object eval(ThreadContext tc, String source) {

	}

	static Object intern(ThreadContext tc, Object o) {
		Runtime rt = getRuntime(tc);
		std::lock_guard<std::mutex> lock(rt->internedObjectsMutex);
		std::unordered_set<Object>::const_iterator i = rt->internedObjects.find(o);
		if (i == rt->internedObjects.end()) {
			// Not found, copy to global memory and insert.
			return nullptr;
		}
		else {
			return *i;
		}
	}

	static String createString(ThreadContext tc, const char* cstr) {
		return nullptr;
	}

	static Uword hash(ThreadContext tc, String s) {
		return 0;
	}

	static Bool equals(ThreadContext tc, String s1, String s2) {
		return False;
	}

	static ThreadContext createThreadContext(Runtime rt, MemoryManager mm, Namespace initialNs) {
		ThreadContext tc = (ThreadContext) allocRaw(mm, sizeof(ThreadContextT));
		tc->runtime = rt;
		tc->memoryManager = mm;
		tc->currentNs = initialNs;
		tls.set(tc);
		return tc;
	}

	static void destroyThreadContext(ThreadContext tc) {
		tls.set(nullptr);
		destroyMemoryManager(tc->memoryManager);
	}

	static ThreadContext getThreadContext() {
		return (ThreadContext) tls.get();
	}

	static Runtime getRuntime(ThreadContext tc) {
		return tc->runtime;
	}

	static MemoryManager getMemoryManager(ThreadContext tc) {
		return tc->memoryManager;
	}

	static Namespace getNamespace(ThreadContext tc) {
		return tc->currentNs;
	}

	static Namespace setNamespace(ThreadContext tc, Namespace ns) {
		Namespace old = tc->currentNs;
		tc->currentNs = ns;
		return old;
	}

	static Exception createException(ThreadContext tc, String message) {

	}

	static String getMessage(ThreadContext tc, Exception e) {

	}

}

// ## 60 C API

#endif
<commit_msg>add more code<commit_after>#ifndef OCTARINE_LANG
#define OCTARINE_LANG

// Table of contents
// ## 00 Macro Definitions
// ## 10 Includes
// ## 20 Type declarations and typedefs
// ## 30 Function declarations
// ## 40 Type definitions
// ## 50 Function definitions
// ## 60 C API

// ## 00 Macro Definitions

#ifdef _WIN32
#define WINDOWS

#ifdef _DEBUG
#define DEBUG
#endif

#ifdef _WIN64
#define OCT64
#else
#define OCT32
#endif

#elif defined (__APPLE__)
#define MACOSX

#ifdef __LP64__
#define OCT64
#else
#define OCT32
#endif

#else
#define LINUX

#ifndef NDEBUG
#define DEBUG
#endif

#ifdef __LP64__
#define OCT64
#else
#define OCT32
#endif

#endif

#define True 1
#define False 0

// ## 10 Includes

#ifdef WINDOWS
#include <Windows.h>
#else
#include <inttypes.h>
#include <pthread.h>
#endif
#include <unordered_map>
#include <unordered_set>
#include <vector>
#include <algorithm>
#include <mutex>

namespace octarine {

	// ## 20 Type declarations and typedefs

#ifdef WINDOWS
	typedef __int8           I8;
	typedef unsigned __int8  U8;
	typedef __int16          I16;
	typedef unsigned __int16 U16;
	typedef __int32          I32;
	typedef unsigned __int32 U32;
	typedef __int64          I64;
	typedef unsigned __int64 U64;
#else
	typedef int8_t   I8;
	typedef uint8_t  U8;
	typedef int16_t  I16;
	typedef uint16_t U16;
	typedef int32_t  I32;
	typedef uint32_t U32;
	typedef int64_t  I64;
	typedef uint64_t U64;
#endif
	typedef float    F32;
	typedef double   F64;
	typedef U8 Bool;
	typedef I32 Char;
#ifdef OCT64
	typedef I64 Word;
	typedef U64 Uword;
#else
	typedef I32 Word;
	typedef U32 Uword;
#endif
	typedef void* Address;

	struct FunctionT;
	typedef FunctionT* Function;

	struct SharedMemoryManagerT;
	typedef SharedMemoryManagerT* SharedMemoryManager;

	struct ThreadMemoryManagerT;
	typedef ThreadMemoryManagerT* ThreadMemoryManager;

	struct NamespaceT;
	typedef NamespaceT* Namespace;

	struct ObjectT;
	typedef ObjectT* Object;

	struct ReaderT;
	typedef ReaderT* Reader;

	struct RuntimeT;
	typedef RuntimeT* Runtime;

	struct StringT;
	typedef StringT* String;

	struct ThreadContextT;
	typedef ThreadContextT* ThreadContext;

	struct TypeT;
	typedef TypeT* Type;

	struct ExceptionT;
	typedef ExceptionT* Exception;

	// ## 30 Function declarations

	static Uword getSize(ThreadContext tc, Type t);

	static SharedMemoryManager createSharedMemoryManager();

	static void destroySharedMemoryManager(SharedMemoryManager mm);

	static ThreadMemoryManager createThreadMemoryManager();

	static void destroyThreadMemoryManager(ThreadMemoryManager mm);

	static Object allocRaw(ThreadMemoryManager mm, Uword size);

	static Object alloc(ThreadContext tc, ThreadMemoryManager mm, Type t);

	static Object allocRaw(SharedMemoryManager mm, Uword size);

	static Object alloc(ThreadContext tc, SharedMemoryManager mm, Type t);

	static Namespace createNamespace(ThreadContext tc, String name);

	static Object bind(ThreadContext tc, Namespace ns, String name, Object obj);

	static Type getNamespaceType(Runtime rt);

	static Bool equals(ThreadContext tc, Namespace ns, Object obj);

	static Type getType(ThreadContext tc, Object obj);

	static Uword hash(ThreadContext tc, Object o);

	static Bool equals(ThreadContext tc, Object o1, Object o2);

	static String makeShared(ThreadContext tc, String s);

	static Namespace makeShared(ThreadContext tc, Namespace s);

	static Runtime createRuntime();

	static void destroyRuntime(Runtime rt);

	static void registerFunction(ThreadContext tc, Namespace ns, Function f);

	static Object eval(ThreadContext tc, String source);

	static Object intern(ThreadContext tc, Object o);

	static String createString(ThreadContext tc, const char* cstr);

	static Uword hash(ThreadContext tc, String s);

	static Bool equals(ThreadContext tc, String s1, String s2);

	static ThreadContext createThreadContext(Runtime rt, ThreadMemoryManager mm, Namespace initialNs);

	static void destroyThreadContext(ThreadContext tc);

	static ThreadContext getThreadContext();

	static Runtime getRuntime(ThreadContext tc);

	static ThreadMemoryManager getThreadMemoryManager(ThreadContext tc);

	static Namespace getNamespace(ThreadContext tc);

	static Namespace setNamespace(ThreadContext tc, Namespace ns);

	static Exception createException(ThreadContext tc, String message);

	static String getMessage(ThreadContext tc, Exception e);

	// ## 40 Type definitions

	// STL Integration
}
namespace std {
	template <>
	struct hash<octarine::String> {
		size_t operator()(const octarine::String& s) const {
			return octarine::hash(octarine::getThreadContext(), s);
		}
	};

	template<>
	struct equal_to<octarine::String> {
		bool operator()(const octarine::String& s1, const octarine::String& s2) const {
			return octarine::equals(octarine::getThreadContext(), s1, s2) == True ? true : false;
		}
	};

	template<>
	struct hash<octarine::Object> {
		size_t operator()(const octarine::Object& o) const {
			return octarine::hash(octarine::getThreadContext(), o);
		}
	};

	template<>
	struct equal_to<octarine::Object> {
		bool operator()(const octarine::Object& o1, const octarine::Object& o2) {
			return octarine::equals(octarine::getThreadContext(), o1, o2) == True ? true : false;
		}
	};
}
namespace octarine {

	// Platform support

	class TLS {
	private:
#ifdef WINDOWS
		DWORD threadLocalStorage;
#elif defined (MACOSX)
		pthread_key_t threadLocalStorage;
#else
#endif
	public:
		TLS() {
#ifdef WINDOWS
			threadLocalStorage = TlsAlloc();
#elif defined (MACOSX)
			pthread_key_create(&threadLocalStorage, nullptr);
#else
#endif
		}

		~TLS() {
#ifdef WINDOWS
			TlsFree(threadLocalStorage);
#elif defined (MACOSX)
			pthread_key_delete(threadLocalStorage);
#else
#endif
		}

		void set(void* value) {
#ifdef WINDOWS
			TlsSetValue(threadLocalStorage, value);
#elif defined (MACOSX)
			pthread_setspecific(threadLocalStorage, value);
#else
#endif
		}

		void* get() {
#ifdef WINDOWS
			return TlsGetValue(threadLocalStorage);
#elif defined (MACOSX)
			return pthread_getspecific(threadLocalStorage);
#else
#endif
		}
	};

	// Octarine types

	struct TypeT {
		Uword size;
	};

	struct ObjectHeader {
		Type type;
		U8 object[0];
	};

	struct SharedMemoryManagerT {
		Uword hello;
	};

	struct ThreadMemoryManagerT {
		Uword hello;
	};

	struct NamespaceT {
		String name;
	};

	struct StringT {
		Uword length;
		U8* utf8data; // Just use platform strings?
	};

	struct BuiltInTypes {
		Type namespaceType;
	};

	struct ThreadContextT {
		Runtime runtime;
		ThreadMemoryManager ThreadMemoryManager;
		Namespace currentNs;
	};

	struct RuntimeT {
		SharedMemoryManager SharedMemoryManager;
		std::mutex namespaceMutex;
		std::unordered_map<String, Namespace> namespaces;
		std::mutex threadContextMutex;
		std::vector<ThreadContext> threadContexts;
		std::mutex internedObjectsMutex;
		std::unordered_set<Object> internedObjects;
		BuiltInTypes builtInTypes;
	};

    // ## 50 Function definitions

	// Global thread local var for getting at the current thread context when it is impossible
	// to pass it along. For example when integrating with the STL or in callbacks from C code.
	static TLS tls;

	static Uword getSize(ThreadContext tc, Type t) {
		return t->size;
	}

	static SharedMemoryManager createSharedMemoryManager() {
		SharedMemoryManager mm = new SharedMemoryManagerT;
		return mm;
	}

	static void destroySharedMemoryManager(SharedMemoryManager mm) {
		delete mm;
	}

	static ThreadMemoryManager createThreadMemoryManager() {
		ThreadMemoryManager mm = new ThreadMemoryManagerT;
		return mm;
	}

	static void destroyThreadMemoryManager(ThreadMemoryManager mm) {
		delete mm;
	}

	static Object allocRaw(SharedMemoryManager mm, Uword size) {
		Uword allocSize = sizeof(ObjectHeader) + size;
		ObjectHeader* oh = (ObjectHeader*)::operator new(allocSize);
		oh->type = nullptr;
		return (Object) &oh->object;
	}

	static Object alloc(ThreadContext tc, SharedMemoryManager mm, Type t) {
		Uword allocSize = sizeof(ObjectHeader) + getSize(tc, t);
		ObjectHeader* oh = (ObjectHeader*)::operator new(allocSize);
		oh->type = t;
		return (Object) &oh->object;
	}

	static Object allocRaw(ThreadMemoryManager mm, Uword size) {
		Uword allocSize = sizeof(ObjectHeader) + size;
		ObjectHeader* oh = (ObjectHeader*)::operator new(allocSize);
		oh->type = nullptr;
		return (Object) &oh->object;
	}

	static Object alloc(ThreadContext tc, ThreadMemoryManager mm, Type t) {
		Uword allocSize = sizeof(ObjectHeader) + getSize(tc, t);
		ObjectHeader* oh = (ObjectHeader*)::operator new(allocSize);
		oh->type = t;
		return (Object) &oh->object;
	}

	static Namespace createNamespace(ThreadContext tc, String name) {
		ThreadMemoryManager mm = getThreadMemoryManager(tc);
		Runtime rt = getRuntime(tc);
		Namespace ns = (Namespace) allocRaw(mm, sizeof(NamespaceT));
		ns->name = name;
		rt->namespaces[name] = ns;
		return makeShared(tc, ns);
	}

	static Object bind(ThreadContext tc, Namespace ns, String name, Object obj) {

	}

	static Type getNamespaceType(Runtime rt) {
		return rt->builtInTypes.namespaceType;
	}

	static Bool equals(ThreadContext tc, Namespace ns, Object obj) {

	}

	static Type getType(ThreadContext tc, Object obj) {

	}

	static Uword hash(ThreadContext tc, Object o) {

	}

	static Bool equals(ThreadContext tc, Object o1, Object o2) {

	}

	static String makeShared(ThreadContext tc, String s) {

	}

	static Namespace makeShared(ThreadContext tc, Namespace s) {

	}

	static Runtime createRuntime() {
		Runtime rt = new RuntimeT;
		// Create main thread context, with its own memory manager
		ThreadContext mainTc = createThreadContext(rt, createThreadMemoryManager(), nullptr);
		rt->threadContexts.push_back(mainTc);
		// Create main namespace
		setNamespace(mainTc, createNamespace(mainTc, createString(mainTc, "octarine")));
		return rt;
	}

	static void destroyRuntime(Runtime rt) {
		std::for_each(rt->threadContexts.begin(), rt->threadContexts.end(), destroyThreadContext);
		delete rt;
	}

	static void registerFunction(ThreadContext tc, Namespace ns, Function f) {

	}

	static Object eval(ThreadContext tc, String source) {

	}

	static Object intern(ThreadContext tc, Object o) {
		Runtime rt = getRuntime(tc);
		std::lock_guard<std::mutex> lock(rt->internedObjectsMutex);
		std::unordered_set<Object>::const_iterator i = rt->internedObjects.find(o);
		if (i == rt->internedObjects.end()) {
			// Not found, copy to global memory and insert.
			return nullptr;
		}
		else {
			return *i;
		}
	}

	static String createString(ThreadContext tc, const char* cstr) {
		return nullptr;
	}

	static Uword hash(ThreadContext tc, String s) {
		return 0;
	}

	static Bool equals(ThreadContext tc, String s1, String s2) {
		return False;
	}

	static ThreadContext createThreadContext(Runtime rt, ThreadMemoryManager mm, Namespace initialNs) {
		ThreadContext tc = (ThreadContext) allocRaw(mm, sizeof(ThreadContextT));
		tc->runtime = rt;
		tc->ThreadMemoryManager = mm;
		tc->currentNs = initialNs;
		tls.set(tc);
		return tc;
	}

	static void destroyThreadContext(ThreadContext tc) {
		tls.set(nullptr);
		destroyThreadMemoryManager(tc->ThreadMemoryManager);
	}

	static ThreadContext getThreadContext() {
		return (ThreadContext) tls.get();
	}

	static Runtime getRuntime(ThreadContext tc) {
		return tc->runtime;
	}

	static ThreadMemoryManager getThreadMemoryManager(ThreadContext tc) {
		return tc->ThreadMemoryManager;
	}

	static Namespace getNamespace(ThreadContext tc) {
		return tc->currentNs;
	}

	static Namespace setNamespace(ThreadContext tc, Namespace ns) {
		Namespace old = tc->currentNs;
		tc->currentNs = ns;
		return old;
	}

	static Exception createException(ThreadContext tc, String message) {

	}

	static String getMessage(ThreadContext tc, Exception e) {

	}

}

// ## 60 C API

#endif
<|endoftext|>


<commit_before>/*

Copyright (c) 2015, Project OSRM contributors
All rights reserved.

Redistribution and use in source and binary forms, with or without modification,
are permitted provided that the following conditions are met:

Redistributions of source code must retain the above copyright notice, this list
of conditions and the following disclaimer.
Redistributions in binary form must reproduce the above copyright notice, this
list of conditions and the following disclaimer in the documentation and/or
other materials provided with the distribution.

THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR
ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

*/

#ifndef MAP_MATCHING_HPP
#define MAP_MATCHING_HPP

#include "routing_base.hpp"

#include "../data_structures/coordinate_calculation.hpp"
#include "../data_structures/hidden_markov_model.hpp"
#include "../util/matching_debug_info.hpp"
#include "../util/simple_logger.hpp"
#include "../util/json_util.hpp"
#include "../util/json_logger.hpp"

#include <osrm/json_container.hpp>
#include <variant/variant.hpp>

#include <fstream>

#include <algorithm>
#include <iomanip>
#include <numeric>

namespace osrm
{
namespace matching
{

struct SubMatching
{
    std::vector<PhantomNode> nodes;
    std::vector<unsigned> indices;
    double length;
    double confidence;
};

using CandidateList = std::vector<std::pair<PhantomNode, double>>;
using CandidateLists = std::vector<CandidateList>;
using HMM = HiddenMarkovModel<CandidateLists>;
using SubMatchingList = std::vector<SubMatching>;

constexpr static const unsigned MAX_BROKEN_STATES = 6;
constexpr static const unsigned MAX_BROKEN_TIME = 30;

constexpr static const double default_beta = 10.0;
constexpr static const double default_sigma_z = 4.07;
}
}

// implements a hidden markov model map matching algorithm
template <class DataFacadeT>
class MapMatching final : public BasicRoutingInterface<DataFacadeT, MapMatching<DataFacadeT>>
{
    using super = BasicRoutingInterface<DataFacadeT, MapMatching<DataFacadeT>>;
    using QueryHeap = SearchEngineData::QueryHeap;
    SearchEngineData &engine_working_data;

  public:
    MapMatching(DataFacadeT *facade, SearchEngineData &engine_working_data)
        : super(facade), engine_working_data(engine_working_data)
    {
    }

    void operator()(const osrm::matching::CandidateLists &candidates_list,
                    const std::vector<FixedPointCoordinate> &trace_coordinates,
                    const std::vector<unsigned> &trace_timestamps,
                    const double matching_beta,
                    const double gps_precision,
                    osrm::matching::SubMatchingList &sub_matchings) const
    {
        BOOST_ASSERT(candidates_list.size() > 0);

        // TODO replace default values with table lookup based on sampling frequency
        EmissionLogProbability emission_log_probability(
            gps_precision > 0 ? gps_precision : osrm::matching::default_sigma_z);
        TransitionLogProbability transition_log_probability(
            matching_beta > 0 ? matching_beta : osrm::matching::default_beta);

        osrm::matching::HMM model(candidates_list, emission_log_probability);

        unsigned initial_timestamp = model.initialize(0);
        if (initial_timestamp == osrm::matching::INVALID_STATE)
        {
            return;
        }

        MatchingDebugInfo matching_debug(osrm::json::Logger::get());
        matching_debug.initialize(candidates_list);

        unsigned breakage_begin = std::numeric_limits<unsigned>::max();
        std::vector<unsigned> split_points;
        std::vector<unsigned> prev_unbroken_timestamps;
        prev_unbroken_timestamps.reserve(candidates_list.size());
        prev_unbroken_timestamps.push_back(initial_timestamp);
        for (auto t = initial_timestamp + 1; t < candidates_list.size(); ++t)
        {
            // breakage recover has removed all previous good points
            bool trace_split = prev_unbroken_timestamps.size() < 1;

            // use temporal information if available to determine a split
            if (trace_timestamps.size() > 0)
            {
                trace_split =
                    trace_split ||
                    (trace_timestamps[t] - trace_timestamps[prev_unbroken_timestamps.back()] >
                     osrm::matching::MAX_BROKEN_TIME);
            }
            else
            {
                trace_split = trace_split || (t - prev_unbroken_timestamps.back() >
                                              osrm::matching::MAX_BROKEN_STATES);
            }

            if (trace_split)
            {
                unsigned split_index = t;
                if (breakage_begin != std::numeric_limits<unsigned>::max())
                {
                    split_index = breakage_begin;
                    breakage_begin = std::numeric_limits<unsigned>::max();
                }
                split_points.push_back(split_index);

                // note: this preserves everything before split_index
                model.clear(split_index);
                unsigned new_start = model.initialize(split_index);
                // no new start was found -> stop viterbi calculation
                if (new_start == osrm::matching::INVALID_STATE)
                {
                    break;
                }
                prev_unbroken_timestamps.clear();
                prev_unbroken_timestamps.push_back(new_start);
                // Important: We potentially go back here!
                // However since t > new_start >= breakge_begin
                // we can only reset trace_coordindates.size() times.
                t = new_start + 1;
            }

            unsigned prev_unbroken_timestamp = prev_unbroken_timestamps.back();

            const auto &prev_viterbi = model.viterbi[prev_unbroken_timestamp];
            const auto &prev_pruned = model.pruned[prev_unbroken_timestamp];
            const auto &prev_unbroken_timestamps_list = candidates_list[prev_unbroken_timestamp];
            const auto &prev_coordinate = trace_coordinates[prev_unbroken_timestamp];

            auto &current_viterbi = model.viterbi[t];
            auto &current_pruned = model.pruned[t];
            auto &current_parents = model.parents[t];
            auto &current_lengths = model.path_lengths[t];
            const auto &current_timestamps_list = candidates_list[t];
            const auto &current_coordinate = trace_coordinates[t];

            engine_working_data.InitializeOrClearFirstThreadLocalStorage(
                super::facade->GetNumberOfNodes());
            engine_working_data.InitializeOrClearSecondThreadLocalStorage(
                super::facade->GetNumberOfNodes());

            QueryHeap &forward_heap = *(engine_working_data.forward_heap_1);
            QueryHeap &reverse_heap = *(engine_working_data.reverse_heap_1);

            // compute d_t for this timestamp and the next one
            for (auto s = 0u; s < prev_viterbi.size(); ++s)
            {
                if (prev_pruned[s])
                    continue;

                for (auto s_prime = 0u; s_prime < current_viterbi.size(); ++s_prime)
                {
                    // how likely is candidate s_prime at time t to be emitted?
                    const double emission_pr =
                        emission_log_probability(candidates_list[t][s_prime].second);
                    double new_value = prev_viterbi[s] + emission_pr;
                    if (current_viterbi[s_prime] > new_value)
                    {
                        continue;
                    }

                    forward_heap.Clear();
                    reverse_heap.Clear();

                    // get distance diff between loc1/2 and locs/s_prime
                    const auto network_distance = super::get_network_distance(
                        forward_heap, reverse_heap, prev_unbroken_timestamps_list[s].first,
                        current_timestamps_list[s_prime].first);
                    const auto great_circle_distance =
                        coordinate_calculation::great_circle_distance(prev_coordinate,
                                                                      current_coordinate);

                    const auto d_t = std::abs(network_distance - great_circle_distance);

                    // very low probability transition -> prune
                    if (d_t > 500)
                    {
                        continue;
                    }

                    const double transition_pr = transition_log_probability(d_t);
                    new_value += transition_pr;

                    matching_debug.add_transition_info(prev_unbroken_timestamp, t, s, s_prime,
                                                       prev_viterbi[s], emission_pr, transition_pr,
                                                       network_distance, great_circle_distance);

                    if (new_value > current_viterbi[s_prime])
                    {
                        current_viterbi[s_prime] = new_value;
                        current_parents[s_prime] = std::make_pair(prev_unbroken_timestamp, s);
                        current_lengths[s_prime] = network_distance;
                        current_pruned[s_prime] = false;
                        model.breakage[t] = false;
                    }
                }
            }

            if (model.breakage[t])
            {
                // save start of breakage -> we need this as split point
                if (t < breakage_begin)
                {
                    breakage_begin = t;
                }

                BOOST_ASSERT(prev_unbroken_timestamps.size() > 0);

                // remove both ends of the breakage
                prev_unbroken_timestamps.pop_back();
            }
            else
            {
                prev_unbroken_timestamps.push_back(t);
            }
        }

        matching_debug.set_viterbi(model.viterbi, model.pruned);

        if (prev_unbroken_timestamps.size() > 0)
        {
            split_points.push_back(prev_unbroken_timestamps.back() + 1);
        }

        unsigned sub_matching_begin = initial_timestamp;
        for (const unsigned sub_matching_end : split_points)
        {
            osrm::matching::SubMatching matching;

            // find real end of trace
            // not sure if this is really needed
            unsigned parent_timestamp_index = sub_matching_end - 1;
            while (parent_timestamp_index >= sub_matching_begin &&
                   model.breakage[parent_timestamp_index])
            {
                parent_timestamp_index--;
            }

            // matchings that only consist of one candidate are invalid
            if (parent_timestamp_index - sub_matching_begin + 1 < 2)
            {
                sub_matching_begin = sub_matching_end;
                continue;
            }

            // loop through the columns, and only compare the last entry
            auto max_element_iter = std::max_element(model.viterbi[parent_timestamp_index].begin(),
                                                     model.viterbi[parent_timestamp_index].end());

            unsigned parent_candidate_index =
                std::distance(model.viterbi[parent_timestamp_index].begin(), max_element_iter);

            std::deque<std::pair<unsigned, unsigned>> reconstructed_indices;
            while (parent_timestamp_index > sub_matching_begin)
            {
                if (model.breakage[parent_timestamp_index])
                {
                    continue;
                }

                reconstructed_indices.emplace_front(parent_timestamp_index, parent_candidate_index);
                const auto &next = model.parents[parent_timestamp_index][parent_candidate_index];
                parent_timestamp_index = next.first;
                parent_candidate_index = next.second;
            }
            reconstructed_indices.emplace_front(parent_timestamp_index, parent_candidate_index);
            if (reconstructed_indices.size() < 2)
            {
                sub_matching_begin = sub_matching_end;
                continue;
            }

            matching.length = 0.0f;
            matching.nodes.resize(reconstructed_indices.size());
            matching.indices.resize(reconstructed_indices.size());
            for (auto i = 0u; i < reconstructed_indices.size(); ++i)
            {
                auto timestamp_index = reconstructed_indices[i].first;
                auto location_index = reconstructed_indices[i].second;

                matching.indices[i] = timestamp_index;
                matching.nodes[i] = candidates_list[timestamp_index][location_index].first;
                matching.length += model.path_lengths[timestamp_index][location_index];

                matching_debug.add_chosen(timestamp_index, location_index);
            }

            sub_matchings.push_back(matching);

            sub_matching_begin = sub_matching_end;
        }

        matching_debug.add_breakage(model.breakage);
    }
};

//[1] "Hidden Markov Map Matching Through Noise and Sparseness"; P. Newson and J. Krumm; 2009; ACM
// GIS

#endif /* MAP_MATCHING_HPP */
<commit_msg>several copyedits to brush up code<commit_after>/*

Copyright (c) 2015, Project OSRM contributors
All rights reserved.

Redistribution and use in source and binary forms, with or without modification,
are permitted provided that the following conditions are met:

Redistributions of source code must retain the above copyright notice, this list
of conditions and the following disclaimer.
Redistributions in binary form must reproduce the above copyright notice, this
list of conditions and the following disclaimer in the documentation and/or
other materials provided with the distribution.

THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR
ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

*/

#ifndef MAP_MATCHING_HPP
#define MAP_MATCHING_HPP

#include "routing_base.hpp"

#include "../data_structures/coordinate_calculation.hpp"
#include "../data_structures/hidden_markov_model.hpp"
#include "../util/matching_debug_info.hpp"
#include "../util/json_logger.hpp"

#include <variant/variant.hpp>

#include <algorithm>
#include <iomanip>
#include <numeric>

namespace osrm
{
namespace matching
{

struct SubMatching
{
    std::vector<PhantomNode> nodes;
    std::vector<unsigned> indices;
    double length;
    double confidence;
};

using CandidateList = std::vector<std::pair<PhantomNode, double>>;
using CandidateLists = std::vector<CandidateList>;
using HMM = HiddenMarkovModel<CandidateLists>;
using SubMatchingList = std::vector<SubMatching>;

constexpr static const unsigned MAX_BROKEN_STATES = 6;
constexpr static const unsigned MAX_BROKEN_TIME = 30;

constexpr static const double default_beta = 10.0;
constexpr static const double default_sigma_z = 4.07;
}
}

// implements a hidden markov model map matching algorithm
template <class DataFacadeT>
class MapMatching final : public BasicRoutingInterface<DataFacadeT, MapMatching<DataFacadeT>>
{
    using super = BasicRoutingInterface<DataFacadeT, MapMatching<DataFacadeT>>;
    using QueryHeap = SearchEngineData::QueryHeap;
    SearchEngineData &engine_working_data;

  public:
    MapMatching(DataFacadeT *facade, SearchEngineData &engine_working_data)
        : super(facade), engine_working_data(engine_working_data)
    {
    }

    void operator()(const osrm::matching::CandidateLists &candidates_list,
                    const std::vector<FixedPointCoordinate> &trace_coordinates,
                    const std::vector<unsigned> &trace_timestamps,
                    const double matching_beta,
                    const double gps_precision,
                    osrm::matching::SubMatchingList &sub_matchings) const
    {
        BOOST_ASSERT(!candidates_list.empty());

        // TODO replace default values with table lookup based on sampling frequency
        EmissionLogProbability emission_log_probability(
            gps_precision > 0. ? gps_precision : osrm::matching::default_sigma_z);
        TransitionLogProbability transition_log_probability(
            matching_beta > 0. ? matching_beta : osrm::matching::default_beta);

        osrm::matching::HMM model(candidates_list, emission_log_probability);

        unsigned initial_timestamp = model.initialize(0);
        if (initial_timestamp == osrm::matching::INVALID_STATE)
        {
            return;
        }

        MatchingDebugInfo matching_debug(osrm::json::Logger::get());
        matching_debug.initialize(candidates_list);

        unsigned breakage_begin = std::numeric_limits<unsigned>::max();
        std::vector<unsigned> split_points;
        std::vector<unsigned> prev_unbroken_timestamps;
        prev_unbroken_timestamps.reserve(candidates_list.size());
        prev_unbroken_timestamps.push_back(initial_timestamp);
        for (auto t = initial_timestamp + 1; t < candidates_list.size(); ++t)
        {
            // breakage recover has removed all previous good points
            bool trace_split = prev_unbroken_timestamps.empty();

            // use temporal information if available to determine a split
            if (!trace_timestamps.empty())
            {
                trace_split =
                    trace_split ||
                    (trace_timestamps[t] - trace_timestamps[prev_unbroken_timestamps.back()] >
                     osrm::matching::MAX_BROKEN_TIME);
            }
            else
            {
                trace_split = trace_split || (t - prev_unbroken_timestamps.back() >
                                              osrm::matching::MAX_BROKEN_STATES);
            }

            if (trace_split)
            {
                unsigned split_index = t;
                if (breakage_begin != std::numeric_limits<unsigned>::max())
                {
                    split_index = breakage_begin;
                    breakage_begin = std::numeric_limits<unsigned>::max();
                }
                split_points.push_back(split_index);

                // note: this preserves everything before split_index
                model.clear(split_index);
                unsigned new_start = model.initialize(split_index);
                // no new start was found -> stop viterbi calculation
                if (new_start == osrm::matching::INVALID_STATE)
                {
                    break;
                }
                prev_unbroken_timestamps.clear();
                prev_unbroken_timestamps.push_back(new_start);
                // Important: We potentially go back here!
                // However since t > new_start >= breakge_begin
                // we can only reset trace_coordindates.size() times.
                t = new_start + 1;
            }

            BOOST_ASSERT(!prev_unbroken_timestamps.empty());
            const unsigned prev_unbroken_timestamp = prev_unbroken_timestamps.back();

            const auto &prev_viterbi = model.viterbi[prev_unbroken_timestamp];
            const auto &prev_pruned = model.pruned[prev_unbroken_timestamp];
            const auto &prev_unbroken_timestamps_list = candidates_list[prev_unbroken_timestamp];
            const auto &prev_coordinate = trace_coordinates[prev_unbroken_timestamp];

            auto &current_viterbi = model.viterbi[t];
            auto &current_pruned = model.pruned[t];
            auto &current_parents = model.parents[t];
            auto &current_lengths = model.path_lengths[t];
            const auto &current_timestamps_list = candidates_list[t];
            const auto &current_coordinate = trace_coordinates[t];

            engine_working_data.InitializeOrClearFirstThreadLocalStorage(
                super::facade->GetNumberOfNodes());
            engine_working_data.InitializeOrClearSecondThreadLocalStorage(
                super::facade->GetNumberOfNodes());

            QueryHeap &forward_heap = *(engine_working_data.forward_heap_1);
            QueryHeap &reverse_heap = *(engine_working_data.reverse_heap_1);

            // compute d_t for this timestamp and the next one
            for (const auto s : osrm::irange<std::size_t>(0u, prev_viterbi.size()))
            {
                if (prev_pruned[s])
                {
                    continue;
                }

                for (const auto s_prime : osrm::irange<std::size_t>(0u, current_viterbi.size()))
                {
                    // how likely is candidate s_prime at time t to be emitted?
                    const double emission_pr =
                        emission_log_probability(candidates_list[t][s_prime].second);
                    double new_value = prev_viterbi[s] + emission_pr;
                    if (current_viterbi[s_prime] > new_value)
                    {
                        continue;
                    }

                    forward_heap.Clear();
                    reverse_heap.Clear();

                    // get distance diff between loc1/2 and locs/s_prime
                    const auto network_distance = super::get_network_distance(
                        forward_heap, reverse_heap, prev_unbroken_timestamps_list[s].first,
                        current_timestamps_list[s_prime].first);
                    const auto great_circle_distance =
                        coordinate_calculation::great_circle_distance(prev_coordinate,
                                                                      current_coordinate);

                    const auto d_t = std::abs(network_distance - great_circle_distance);

                    // very low probability transition -> prune
                    if (d_t > 500)
                    {
                        continue;
                    }

                    const double transition_pr = transition_log_probability(d_t);
                    new_value += transition_pr;

                    matching_debug.add_transition_info(prev_unbroken_timestamp, t, s, s_prime,
                                                       prev_viterbi[s], emission_pr, transition_pr,
                                                       network_distance, great_circle_distance);

                    if (new_value > current_viterbi[s_prime])
                    {
                        current_viterbi[s_prime] = new_value;
                        current_parents[s_prime] = std::make_pair(prev_unbroken_timestamp, s);
                        current_lengths[s_prime] = network_distance;
                        current_pruned[s_prime] = false;
                        model.breakage[t] = false;
                    }
                }
            }

            if (model.breakage[t])
            {
                // save start of breakage -> we need this as split point
                if (t < breakage_begin)
                {
                    breakage_begin = t;
                }

                BOOST_ASSERT(prev_unbroken_timestamps.size() > 0);
                // remove both ends of the breakage
                prev_unbroken_timestamps.pop_back();
            }
            else
            {
                prev_unbroken_timestamps.push_back(t);
            }
        }

        matching_debug.set_viterbi(model.viterbi, model.pruned);

        if (!prev_unbroken_timestamps.empty())
        {
            split_points.push_back(prev_unbroken_timestamps.back() + 1);
        }

        unsigned sub_matching_begin = initial_timestamp;
        for (const unsigned sub_matching_end : split_points)
        {
            osrm::matching::SubMatching matching;

            // find real end of trace
            // not sure if this is really needed
            unsigned parent_timestamp_index = sub_matching_end - 1;
            while (parent_timestamp_index >= sub_matching_begin &&
                   model.breakage[parent_timestamp_index])
            {
                parent_timestamp_index--;
            }

            // matchings that only consist of one candidate are invalid
            if (parent_timestamp_index - sub_matching_begin + 1 < 2)
            {
                sub_matching_begin = sub_matching_end;
                continue;
            }

            // loop through the columns, and only compare the last entry
            const auto max_element_iter =
                std::max_element(model.viterbi[parent_timestamp_index].begin(),
                                 model.viterbi[parent_timestamp_index].end());

            unsigned parent_candidate_index =
                std::distance(model.viterbi[parent_timestamp_index].begin(), max_element_iter);

            std::deque<std::pair<unsigned, unsigned>> reconstructed_indices;
            while (parent_timestamp_index > sub_matching_begin)
            {
                if (model.breakage[parent_timestamp_index])
                {
                    continue;
                }

                reconstructed_indices.emplace_front(parent_timestamp_index, parent_candidate_index);
                const auto &next = model.parents[parent_timestamp_index][parent_candidate_index];
                parent_timestamp_index = next.first;
                parent_candidate_index = next.second;
            }
            reconstructed_indices.emplace_front(parent_timestamp_index, parent_candidate_index);
            if (reconstructed_indices.size() < 2)
            {
                sub_matching_begin = sub_matching_end;
                continue;
            }

            matching.length = 0.0f;
            matching.nodes.resize(reconstructed_indices.size());
            matching.indices.resize(reconstructed_indices.size());
            for (const auto i : osrm::irange<std::size_t>(0u, reconstructed_indices.size()))
            {
                auto timestamp_index = reconstructed_indices[i].first;
                auto location_index = reconstructed_indices[i].second;

                matching.indices[i] = timestamp_index;
                matching.nodes[i] = candidates_list[timestamp_index][location_index].first;
                matching.length += model.path_lengths[timestamp_index][location_index];

                matching_debug.add_chosen(timestamp_index, location_index);
            }

            sub_matchings.push_back(matching);
            sub_matching_begin = sub_matching_end;
        }
        matching_debug.add_breakage(model.breakage);
    }
};

//[1] "Hidden Markov Map Matching Through Noise and Sparseness"; P. Newson and J. Krumm; 2009; ACM
// GIS

#endif /* MAP_MATCHING_HPP */
<|endoftext|>


<commit_before>/*
 * Copyright (c) 2013 Eugene Lazin <4lazin@gmail.com>
 *
 * This program is free software; you can redistribute it and/or modify it
 * under the terms of the GNU General Public License as published by the Free
 * Software Foundation; either version 2 of the License, or (at your option)
 * any later version.
 *
 */

#include "storage.h"
#include "util.h"
#include <stdexcept>
#include <algorithm>
#include <atomic>
#include <sstream>
#include <cassert>
#include <apr_general.h>
#include <apr_mmap.h>
#include <log4cxx/logger.h>
#include <log4cxx/logmanager.h>
#include <boost/property_tree/ptree.hpp>
#include <boost/property_tree/json_parser.hpp>

namespace Akumuli {

static log4cxx::LoggerPtr s_logger_ = log4cxx::LogManager::getLogger("Akumuli.Storage");

//----------------------------------Volume----------------------------------------------

Volume::Volume(const char* file_name)
    : mmap_(file_name)
{
    mmap_.throw_if_bad();  // panic if can't mmap volume
    page_ = reinterpret_cast<PageHeader*>(mmap_.get_pointer());
}

PageHeader* Volume::get_page() const noexcept {
    return page_;
}

PageHeader* Volume::reallocate_disc_space() {
    uint32_t page_id = page_->page_id;
    uint32_t overwrites_count = page_->overwrites_count;
    PageType page_type = page_->type;
    mmap_.remap_file_destructive();
    page_ = new (mmap_.get_pointer()) PageHeader(page_type, 0, mmap_.get_size(), page_id);
    page_->overwrites_count = overwrites_count;
    return page_;
}

//----------------------------------Storage---------------------------------------------

Storage::Storage(const char* file_name)
{
    /* Exception, thrown from this c-tor means that something really bad
     * happend and we it's impossible to open this storage, for example -
     * because metadata file is corrupted, or volume is missed on disc.
     */

    // 1. Read json file
    boost::property_tree::ptree ptree;
    // NOTE: there is a known bug in boost 1.49 - https://svn.boost.org/trac/boost/ticket/6785
    // FIX: sed -i -e 's/std::make_pair(c.name, Str(b, e))/std::make_pair(c.name, Ptree(Str(b, e)))/' json_parser_read.hpp
    boost::property_tree::json_parser::read_json(file_name, ptree);

    // 2. Read volumes
    int num_volumes = ptree.get_child("num_volumes").get_value(0);
    if (num_volumes == 0) {
        throw std::runtime_error("Invalid storage");
    }

    std::vector<std::string> volume_names(num_volumes);
    for(auto child_node: ptree.get_child("volumes")) {
        auto volume_index = child_node.second.get_child("index").get_value_optional<int>();
        auto volume_path = child_node.second.get_child("path").get_value_optional<std::string>();
        if (volume_index && volume_path) {
            volume_names.at(*volume_index) = *volume_path;
        }
        else {
            throw std::runtime_error("Invalid storage, bad volume link");
        }
    }

    // check result
    for(std::string const& path: volume_names) {
        if (path.empty())
            throw std::runtime_error("Invalid storage, one of the volumes is missing");
    }

    // create volumes list
    for(auto path: volume_names) {
        Volume* vol = new Volume(path.c_str());
        volumes_.push_back(vol);
    }

    // volume with maximal overwrites_count and minimal index must be active
    int max_index = -1;
    int64_t max_overwrites = -1;
    for(int i = 0; i < num_volumes; i++) {
        PageHeader* page = volumes_.at(i)->get_page();
        if (static_cast<int64_t>(page->overwrites_count) >= max_overwrites) {
            max_overwrites = static_cast<int64_t>(page->overwrites_count);
            max_index = i;
        }
    }

    active_volume_ = volumes_.at(max_index);
    active_page_ = active_volume_->get_page();
}

// Reading

void Storage::find_entry(ParamId param, TimeStamp timestamp) {
    int64_t raw_time = timestamp.precise;
}

// Writing

//! commit changes
void Storage::commit() {
    // TODO: volume->flush()
}

//! write data
int Storage::write(Entry const& entry) {
    // FIXME: this code intentionaly single threaded
    while(true) {
        int status = active_page_->add_entry(entry);
        switch (status) {
        case AKU_WRITE_STATUS_OVERFLOW:
            // select next page in round robin order
            active_volume_index_++;
            active_volume_ = volumes_[active_volume_index_ % volumes_.size()];
            active_page_ = active_volume_->reallocate_disc_space();
            active_page_->clear();
            break;
        default:
            return status;
        };
    }
}

int Storage::write(Entry2 const& entry) {
    // FIXME: this code intentionaly left single threaded
    while(true) {
        int status = active_page_->add_entry(entry);
        switch (status) {
        case AKU_WRITE_STATUS_OVERFLOW:
            // select next page in round robin order
            active_volume_index_++;
            active_volume_ = volumes_[active_volume_index_ % volumes_.size()];
            active_page_ = active_volume_->reallocate_disc_space();
            active_page_->clear();
            break;
        default:
            return status;
        };
    }
}

/** This function creates file with specified size
  */
static apr_status_t create_file(const char* file_name, uint64_t size) {
    apr_status_t status;
    int success_count = 0;
    apr_pool_t* mem_pool = NULL;
    apr_file_t* file = NULL;

    status = apr_pool_create(&mem_pool, NULL);
    if (status == APR_SUCCESS) {
        success_count++;

        // Create new file
        status = apr_file_open(&file, file_name, APR_CREATE|APR_WRITE, APR_OS_DEFAULT, mem_pool);
        if (status == APR_SUCCESS) {
            success_count++;

            // Truncate file
            status = apr_file_trunc(file, size);
            if (status == APR_SUCCESS)
                success_count++;
        }
    }

    if (status != APR_SUCCESS) {
        char error_message[0x100];
        apr_strerror(status, error_message, 0x100);
        LOG4CXX_ERROR(s_logger_,  "Can't create file, error " << error_message << " on step " << success_count);
    }

    switch(success_count) {
    case 3:
    case 2:
        status = apr_file_close(file);
    case 1:
        apr_pool_destroy(mem_pool);
    case 0:
        // even apr pool is not created
        break;
    }
    return status;
}

/** This function creates one of the page files with specified
  * name and index.
  */
static apr_status_t create_page_file(const char* file_name, uint32_t page_index) {
    apr_status_t status;
    int64_t size = AKU_MAX_PAGE_SIZE;

    status = create_file(file_name, size);
    if (status != APR_SUCCESS) {
        LOG4CXX_ERROR(s_logger_, "Can't create page file " << file_name);
        return status;
    }

    MemoryMappedFile mfile(file_name);
    if (mfile.is_bad())
        return mfile.status_code();

    // Create index page
    auto index_ptr = mfile.get_pointer();
    auto index_page = new (index_ptr) PageHeader(PageType::Index, 0, AKU_MAX_PAGE_SIZE, page_index);

    // FIXME: revisit - is it actually needed?
    // Activate the first page
    if (page_index == 0) {
        index_page->clear();
    }
    return status;
}

/** Create page files, return list of statuses.
  */
static std::vector<apr_status_t> create_page_files(std::vector<std::string> const& targets) {
    std::vector<apr_status_t> results(targets.size(), APR_SUCCESS);
    for (size_t ix = 0; ix < targets.size(); ix++) {
        apr_status_t res = create_page_file(targets[ix].c_str(), ix);
        results[ix] = res;
    }
    return results;
}

static std::vector<apr_status_t> delete_files(const std::vector<std::string>& targets, const std::vector<apr_status_t>& statuses) {
    if (targets.size() != statuses.size()) {
        throw std::logic_error("Sizes of targets and statuses doesn't match");
    }
    apr_pool_t* mem_pool = NULL;
    int op_count = 0;
    apr_status_t status = apr_pool_create(&mem_pool, NULL);
    std::vector<apr_status_t> results;
    if (status == APR_SUCCESS) {
        op_count++;
        for(auto ix = 0; ix < targets.size(); ix++) {
            const std::string& target = targets[ix];
            if (statuses[ix] == APR_SUCCESS) {
                LOG4CXX_INFO(s_logger_, "Removing " << target);
                status = apr_file_remove(target.c_str(), mem_pool);
                results.push_back(status);
                if (status != APR_SUCCESS) {
                    char error_message[1024];
                    apr_strerror(status, error_message, 1024);
                    LOG4CXX_ERROR(s_logger_, "Error [" << error_message << "] while deleting a file " << target);
                }
            }
            else {
                LOG4CXX_INFO(s_logger_, "Target " << target << " doesn't need to be removed");
            }
        }
    }
    if (op_count) {
        apr_pool_destroy(mem_pool);
    }
    return results;
}

/** This function creates metadata file - root of the storage system.
  * This page contains creation date and time, number of pages,
  * all the page file names and they order.
  */
static apr_status_t create_metadata_page( const char* file_name
                                        , std::vector<std::string> const& page_file_names)
{
    try {
        boost::property_tree::ptree root;
        auto now = apr_time_now();
        char date_time[0x100];
        apr_rfc822_date(date_time, now);
        root.add("creation_time", date_time);
        root.add("num_volumes", page_file_names.size());
        boost::property_tree::ptree volumes_list;
        for(size_t i = 0; i < page_file_names.size(); i++) {
            boost::property_tree::ptree page_desc;
            page_desc.add("index", i);
            page_desc.add("path", page_file_names[i]);
            volumes_list.push_back(std::make_pair("", page_desc));
        }
        root.add_child("volumes", volumes_list);
        boost::property_tree::json_parser::write_json(file_name, root);
    }
    catch(const std::exception& err) {
        LOG4CXX_ERROR(s_logger_, "Can't generate JSON file " << file_name <<
                                 ", the error is: " << err.what());
        return APR_EGENERAL;
    }
    return APR_SUCCESS;
}

apr_status_t Storage::new_storage( const char* 	file_name
                                 , const char* 	metadata_path
                                 , const char* 	volumes_path
                                 , int          num_pages
                                 )
{
    apr_pool_t* mempool;
    apr_status_t status = apr_pool_create(&mempool, NULL);
    if (status != APR_SUCCESS)
        return status;

    // calculate list of page-file names
    std::vector<std::string> page_names;
    for (int ix = 0; ix < num_pages; ix++) {
        std::stringstream stream;
        stream << file_name << "_" << ix << ".volume";
        char* path = nullptr;
        std::string volume_file_name = stream.str();
        status = apr_filepath_merge(&path, volumes_path, volume_file_name.c_str(), APR_FILEPATH_NATIVE, mempool);
        if (status != APR_SUCCESS) {
            auto error_message = apr_error_message(status);
            LOG4CXX_ERROR(s_logger_, "Invalid volumes path: " << error_message);
            apr_pool_destroy(mempool);
            throw AprException(status, error_message.c_str());
        }
        page_names.push_back(path);
    }

    apr_pool_clear(mempool);

    std::vector<apr_status_t> page_creation_statuses = create_page_files(page_names);
    for(auto creation_status: page_creation_statuses) {
        if (creation_status != APR_SUCCESS) {
            LOG4CXX_ERROR(s_logger_, "Not all pages successfullly created. Cleaning up.");
            apr_pool_destroy(mempool);
            delete_files(page_names, page_creation_statuses);
            return creation_status;
        }
    }

    std::stringstream stream;
    stream << file_name << ".akumuli";
    char* path = nullptr;
    std::string metadata_file_name = stream.str();
    status = apr_filepath_merge(&path, metadata_path, metadata_file_name.c_str(), APR_FILEPATH_NATIVE, mempool);
    if (status != APR_SUCCESS) {
        auto error_message = apr_error_message(status);
        LOG4CXX_ERROR(s_logger_, "Invalid metadata path: " << error_message);
        apr_pool_destroy(mempool);
        throw AprException(status, error_message.c_str());
    }
    status = create_metadata_page(path, page_names);
    apr_pool_destroy(mempool);
    return status;
}

}
<commit_msg>Pages overwrite order fixed<commit_after>/*
 * Copyright (c) 2013 Eugene Lazin <4lazin@gmail.com>
 *
 * This program is free software; you can redistribute it and/or modify it
 * under the terms of the GNU General Public License as published by the Free
 * Software Foundation; either version 2 of the License, or (at your option)
 * any later version.
 *
 */

#include "storage.h"
#include "util.h"
#include <stdexcept>
#include <algorithm>
#include <atomic>
#include <sstream>
#include <cassert>
#include <apr_general.h>
#include <apr_mmap.h>
#include <log4cxx/logger.h>
#include <log4cxx/logmanager.h>
#include <boost/property_tree/ptree.hpp>
#include <boost/property_tree/json_parser.hpp>

namespace Akumuli {

static log4cxx::LoggerPtr s_logger_ = log4cxx::LogManager::getLogger("Akumuli.Storage");

//----------------------------------Volume----------------------------------------------

Volume::Volume(const char* file_name)
    : mmap_(file_name)
{
    mmap_.throw_if_bad();  // panic if can't mmap volume
    page_ = reinterpret_cast<PageHeader*>(mmap_.get_pointer());
}

PageHeader* Volume::get_page() const noexcept {
    return page_;
}

PageHeader* Volume::reallocate_disc_space() {
    uint32_t page_id = page_->page_id;
    uint32_t overwrites_count = page_->overwrites_count;
    PageType page_type = page_->type;
    mmap_.remap_file_destructive();
    page_ = new (mmap_.get_pointer()) PageHeader(page_type, 0, mmap_.get_size(), page_id);
    page_->overwrites_count = overwrites_count;
    return page_;
}

//----------------------------------Storage---------------------------------------------

Storage::Storage(const char* file_name)
{
    /* Exception, thrown from this c-tor means that something really bad
     * happend and we it's impossible to open this storage, for example -
     * because metadata file is corrupted, or volume is missed on disc.
     */

    // 1. Read json file
    boost::property_tree::ptree ptree;
    // NOTE: there is a known bug in boost 1.49 - https://svn.boost.org/trac/boost/ticket/6785
    // FIX: sed -i -e 's/std::make_pair(c.name, Str(b, e))/std::make_pair(c.name, Ptree(Str(b, e)))/' json_parser_read.hpp
    boost::property_tree::json_parser::read_json(file_name, ptree);

    // 2. Read volumes
    int num_volumes = ptree.get_child("num_volumes").get_value(0);
    if (num_volumes == 0) {
        throw std::runtime_error("Invalid storage");
    }

    std::vector<std::string> volume_names(num_volumes);
    for(auto child_node: ptree.get_child("volumes")) {
        auto volume_index = child_node.second.get_child("index").get_value_optional<int>();
        auto volume_path = child_node.second.get_child("path").get_value_optional<std::string>();
        if (volume_index && volume_path) {
            volume_names.at(*volume_index) = *volume_path;
        }
        else {
            throw std::runtime_error("Invalid storage, bad volume link");
        }
    }

    // check result
    for(std::string const& path: volume_names) {
        if (path.empty())
            throw std::runtime_error("Invalid storage, one of the volumes is missing");
    }

    // create volumes list
    for(auto path: volume_names) {
        Volume* vol = new Volume(path.c_str());
        volumes_.push_back(vol);
    }

    // volume with max overwrites_count and max index must be active
    int max_index = -1;
    int64_t max_overwrites = -1;
    for(int i = 0; i < num_volumes; i++) {
        PageHeader* page = volumes_.at(i)->get_page();
        if (static_cast<int64_t>(page->overwrites_count) >= max_overwrites) {
            max_overwrites = static_cast<int64_t>(page->overwrites_count);
            max_index = i;
        }
    }

    active_volume_index_ = max_index;
    active_volume_ = volumes_.at(max_index);
    active_page_ = active_volume_->get_page();
}

// Reading

void Storage::find_entry(ParamId param, TimeStamp timestamp) {
    int64_t raw_time = timestamp.precise;
}

// Writing

//! commit changes
void Storage::commit() {
    // TODO: volume->flush()
}

//! write data
int Storage::write(Entry const& entry) {
    // FIXME: this code intentionaly single threaded
    while(true) {
        int status = active_page_->add_entry(entry);
        switch (status) {
        case AKU_WRITE_STATUS_OVERFLOW:
            // select next page in round robin order
            active_volume_index_++;
            active_volume_ = volumes_[active_volume_index_ % volumes_.size()];
            active_page_ = active_volume_->reallocate_disc_space();
            active_page_->clear();
            break;
        default:
            return status;
        };
    }
}

int Storage::write(Entry2 const& entry) {
    // FIXME: this code intentionaly left single threaded
    while(true) {
        int status = active_page_->add_entry(entry);
        switch (status) {
        case AKU_WRITE_STATUS_OVERFLOW:
            // select next page in round robin order
            active_volume_index_++;
            active_volume_ = volumes_[active_volume_index_ % volumes_.size()];
            active_page_ = active_volume_->reallocate_disc_space();
            active_page_->clear();
            break;
        default:
            return status;
        };
    }
}

/** This function creates file with specified size
  */
static apr_status_t create_file(const char* file_name, uint64_t size) {
    apr_status_t status;
    int success_count = 0;
    apr_pool_t* mem_pool = NULL;
    apr_file_t* file = NULL;

    status = apr_pool_create(&mem_pool, NULL);
    if (status == APR_SUCCESS) {
        success_count++;

        // Create new file
        status = apr_file_open(&file, file_name, APR_CREATE|APR_WRITE, APR_OS_DEFAULT, mem_pool);
        if (status == APR_SUCCESS) {
            success_count++;

            // Truncate file
            status = apr_file_trunc(file, size);
            if (status == APR_SUCCESS)
                success_count++;
        }
    }

    if (status != APR_SUCCESS) {
        char error_message[0x100];
        apr_strerror(status, error_message, 0x100);
        LOG4CXX_ERROR(s_logger_,  "Can't create file, error " << error_message << " on step " << success_count);
    }

    switch(success_count) {
    case 3:
    case 2:
        status = apr_file_close(file);
    case 1:
        apr_pool_destroy(mem_pool);
    case 0:
        // even apr pool is not created
        break;
    }
    return status;
}

/** This function creates one of the page files with specified
  * name and index.
  */
static apr_status_t create_page_file(const char* file_name, uint32_t page_index) {
    apr_status_t status;
    int64_t size = AKU_MAX_PAGE_SIZE;

    status = create_file(file_name, size);
    if (status != APR_SUCCESS) {
        LOG4CXX_ERROR(s_logger_, "Can't create page file " << file_name);
        return status;
    }

    MemoryMappedFile mfile(file_name);
    if (mfile.is_bad())
        return mfile.status_code();

    // Create index page
    auto index_ptr = mfile.get_pointer();
    auto index_page = new (index_ptr) PageHeader(PageType::Index, 0, AKU_MAX_PAGE_SIZE, page_index);

    // FIXME: revisit - is it actually needed?
    // Activate the first page
    if (page_index == 0) {
        index_page->clear();
    }
    return status;
}

/** Create page files, return list of statuses.
  */
static std::vector<apr_status_t> create_page_files(std::vector<std::string> const& targets) {
    std::vector<apr_status_t> results(targets.size(), APR_SUCCESS);
    for (size_t ix = 0; ix < targets.size(); ix++) {
        apr_status_t res = create_page_file(targets[ix].c_str(), ix);
        results[ix] = res;
    }
    return results;
}

static std::vector<apr_status_t> delete_files(const std::vector<std::string>& targets, const std::vector<apr_status_t>& statuses) {
    if (targets.size() != statuses.size()) {
        throw std::logic_error("Sizes of targets and statuses doesn't match");
    }
    apr_pool_t* mem_pool = NULL;
    int op_count = 0;
    apr_status_t status = apr_pool_create(&mem_pool, NULL);
    std::vector<apr_status_t> results;
    if (status == APR_SUCCESS) {
        op_count++;
        for(auto ix = 0; ix < targets.size(); ix++) {
            const std::string& target = targets[ix];
            if (statuses[ix] == APR_SUCCESS) {
                LOG4CXX_INFO(s_logger_, "Removing " << target);
                status = apr_file_remove(target.c_str(), mem_pool);
                results.push_back(status);
                if (status != APR_SUCCESS) {
                    char error_message[1024];
                    apr_strerror(status, error_message, 1024);
                    LOG4CXX_ERROR(s_logger_, "Error [" << error_message << "] while deleting a file " << target);
                }
            }
            else {
                LOG4CXX_INFO(s_logger_, "Target " << target << " doesn't need to be removed");
            }
        }
    }
    if (op_count) {
        apr_pool_destroy(mem_pool);
    }
    return results;
}

/** This function creates metadata file - root of the storage system.
  * This page contains creation date and time, number of pages,
  * all the page file names and they order.
  */
static apr_status_t create_metadata_page( const char* file_name
                                        , std::vector<std::string> const& page_file_names)
{
    try {
        boost::property_tree::ptree root;
        auto now = apr_time_now();
        char date_time[0x100];
        apr_rfc822_date(date_time, now);
        root.add("creation_time", date_time);
        root.add("num_volumes", page_file_names.size());
        boost::property_tree::ptree volumes_list;
        for(size_t i = 0; i < page_file_names.size(); i++) {
            boost::property_tree::ptree page_desc;
            page_desc.add("index", i);
            page_desc.add("path", page_file_names[i]);
            volumes_list.push_back(std::make_pair("", page_desc));
        }
        root.add_child("volumes", volumes_list);
        boost::property_tree::json_parser::write_json(file_name, root);
    }
    catch(const std::exception& err) {
        LOG4CXX_ERROR(s_logger_, "Can't generate JSON file " << file_name <<
                                 ", the error is: " << err.what());
        return APR_EGENERAL;
    }
    return APR_SUCCESS;
}

apr_status_t Storage::new_storage( const char* 	file_name
                                 , const char* 	metadata_path
                                 , const char* 	volumes_path
                                 , int          num_pages
                                 )
{
    apr_pool_t* mempool;
    apr_status_t status = apr_pool_create(&mempool, NULL);
    if (status != APR_SUCCESS)
        return status;

    // calculate list of page-file names
    std::vector<std::string> page_names;
    for (int ix = 0; ix < num_pages; ix++) {
        std::stringstream stream;
        stream << file_name << "_" << ix << ".volume";
        char* path = nullptr;
        std::string volume_file_name = stream.str();
        status = apr_filepath_merge(&path, volumes_path, volume_file_name.c_str(), APR_FILEPATH_NATIVE, mempool);
        if (status != APR_SUCCESS) {
            auto error_message = apr_error_message(status);
            LOG4CXX_ERROR(s_logger_, "Invalid volumes path: " << error_message);
            apr_pool_destroy(mempool);
            throw AprException(status, error_message.c_str());
        }
        page_names.push_back(path);
    }

    apr_pool_clear(mempool);

    std::vector<apr_status_t> page_creation_statuses = create_page_files(page_names);
    for(auto creation_status: page_creation_statuses) {
        if (creation_status != APR_SUCCESS) {
            LOG4CXX_ERROR(s_logger_, "Not all pages successfullly created. Cleaning up.");
            apr_pool_destroy(mempool);
            delete_files(page_names, page_creation_statuses);
            return creation_status;
        }
    }

    std::stringstream stream;
    stream << file_name << ".akumuli";
    char* path = nullptr;
    std::string metadata_file_name = stream.str();
    status = apr_filepath_merge(&path, metadata_path, metadata_file_name.c_str(), APR_FILEPATH_NATIVE, mempool);
    if (status != APR_SUCCESS) {
        auto error_message = apr_error_message(status);
        LOG4CXX_ERROR(s_logger_, "Invalid metadata path: " << error_message);
        apr_pool_destroy(mempool);
        throw AprException(status, error_message.c_str());
    }
    status = create_metadata_page(path, page_names);
    apr_pool_destroy(mempool);
    return status;
}

}
<|endoftext|>


<commit_before>#include "databaseconnectiondatadialog.hxx"

#include <apps/app/app.hxx>
#include <apps/app/settings.hxx>

#include <ui/widgets/gridgroupbox.hxx>
#include <ui/widgets/databaseconnectionstatuslabel.hxx>
#include <ui/widgets/fieldlabel.hxx>
#include <ui/widgets/hostnameedit.hxx>

PROJECT_NAMESPACE_BEGIN
WIDGETS_NAMESPACE_BEGIN

DatabaseConnectionDataDialog::DatabaseConnectionDataDialog(QWidget *p)
    : VerticalDialog(QObject::tr("Database Connection"), p)
{
    m_cd = APP_NAMESPACE::Settings().loadConnectionData();

    auto status = createStatusWidget<DatabaseConnectionStatusLabel>();
    status->onDatabaseStatusChanged(PGCONN_NAMESPACE::Connection::ConnectionStates::Disconnected, tr("Not connected."));

    createWidgets();
    createButtons();
}

void DatabaseConnectionDataDialog::accept()
{
    APP_NAMESPACE::Settings s;
    s.saveConnectionData(m_cd);

    done(QDialog::Accepted);
}

void DatabaseConnectionDataDialog::createWidgets()
{
    m_box = createContentWidget<GridGroupBox>();
    m_box->setTitle(tr("&Database Connection Database"));

    m_host = m_box->createLabelledSingleRowWidget<HostNameEdit>(tr("&Host"), QString::fromStdString(m_cd.hostName()));
}

WIDGETS_NAMESPACE_END
PROJECT_NAMESPACE_END
<commit_msg>added HostEdit<commit_after>#include "databaseconnectiondatadialog.hxx"

#include <apps/app/app.hxx>
#include <apps/app/settings.hxx>

#include <ui/widgets/gridgroupbox.hxx>
#include <ui/widgets/databaseconnectionstatuslabel.hxx>
#include <ui/widgets/fieldlabel.hxx>
#include <ui/widgets/hostnameedit.hxx>

PROJECT_NAMESPACE_BEGIN
WIDGETS_NAMESPACE_BEGIN

DatabaseConnectionDataDialog::DatabaseConnectionDataDialog(QWidget *p)
    : VerticalDialog(QObject::tr("Database Connection"), p)
{
    m_cd = APP_NAMESPACE::Settings().loadConnectionData();

    auto status = createStatusWidget<DatabaseConnectionStatusLabel>();
    status->onDatabaseStatusChanged(PGCONN_NAMESPACE::Connection::ConnectionStates::Disconnected, tr("Not connected."));

    createWidgets();
    createButtons();
}

void DatabaseConnectionDataDialog::accept()
{
    APP_NAMESPACE::Settings s;
    s.saveConnectionData(m_cd);

    done(QDialog::Accepted);
}

void DatabaseConnectionDataDialog::createWidgets()
{
    m_box = createContentWidget<GridGroupBox>();
    m_box->setTitle(tr("&Database Connection Database"));

    m_host = m_box->createLabelledSingleRowWidget<HostNameEdit>(tr("&Host"), QString::fromStdString(m_cd.hostName()));
    connect(m_host, &QLineEdit::textChanged, [=](const QString& val) { m_cd.setHostName(val.toStdString()); });
}

WIDGETS_NAMESPACE_END
PROJECT_NAMESPACE_END
<|endoftext|>