<commit_before>/* RTcmix - Copyright (C) 2005 The RTcmix Development Team
   See ``AUTHORS'' for a list of contributors. See ``LICENSE'' for
   the license to this software and for a DISCLAIMER OF ALL WARRANTIES.
*/
// The set_option function, called from a script, lets the user override
// default options (and those stored in the .rtcmixrc file).  The options
// are kept in the <options> object.  See Option.h for advice on how to add
// new options.
//
// Please keep the items created in the steps above in the same order that they
// appear in Option.h -- makes this file easier to maintain.
//
// -JGG, 6/30/04, rev. 4/10/05

#include <RTcmix.h>
#include <ugens.h>		// for die()
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <ctype.h>
#include <errno.h>
#include <Option.h>

enum ParamType {
	AUDIO,
	PLAY,
	RECORD,
	CLOBBER,
	PRINT,
	MUTE_THRESHOLD,
	REPORT_CLIPPING,
	CHECK_PEAKS,
	FULL_DUPLEX,
	EXIT_ON_ERROR,
    BAIL_ON_ERROR,
	AUTO_LOAD,
	FAST_UPDATE,
	REQUIRE_SAMPLE_RATE,
	BUFFER_FRAMES,
	BUFFER_COUNT,
	OSC_INPORT,
	DEVICE,
	INDEVICE,
	OUTDEVICE,
	MIDI_INDEVICE,
	MIDI_OUTDEVICE,
	OSC_HOST,
	DSOPATH,
	RCNAME
};

#define OPT_STRLEN 128

struct Param {
	char arg[OPT_STRLEN];
	ParamType type;
	bool value;		// use false if not relevant, i.e. for key=value style
};

static Param _param_list[] = {
	// These are key=value option strings.  Please list these in the order in
	// which they appear in Option.h, to make it easier to compare.

	// bool options
	{ kOptionAudio, AUDIO, false},
	{ kOptionPlay, PLAY, false},
	{ kOptionRecord, RECORD, false},
	{ kOptionClobber, CLOBBER, false},
	{ kOptionReportClipping, REPORT_CLIPPING, false},
	{ kOptionCheckPeaks, CHECK_PEAKS, false},
	{ kOptionExitOnError, EXIT_ON_ERROR, false},
    { kOptionBailOnError, BAIL_ON_ERROR, false},
	{ kOptionAutoLoad, AUTO_LOAD, false},
	{ kOptionFastUpdate, FAST_UPDATE, false},
	{ kOptionRequireSampleRate, REQUIRE_SAMPLE_RATE, true},

	// number options
	{ kOptionBufferFrames, BUFFER_FRAMES, false},
	{ kOptionBufferCount, BUFFER_COUNT, false},
	{ kOptionOSCInPort, OSC_INPORT, false},
	{ kOptionPrint, PRINT, false},
	{ kOptionMuteThreshold, MUTE_THRESHOLD, false},

	// string options
	{ kOptionDevice, DEVICE, false},
	{ kOptionInDevice, INDEVICE, false},
	{ kOptionOutDevice, OUTDEVICE, false},
	{ kOptionMidiInDevice, MIDI_INDEVICE, false},
	{ kOptionMidiOutDevice, MIDI_OUTDEVICE, false},
	{ kOptionOSCHost, OSC_HOST, false},
	{ kOptionDSOPath, DSOPATH, false},
	{ kOptionRCName, RCNAME, false},

	// These are the deprecated single-value option strings.
	// Please don't add more.
	{ "AUDIO_ON", AUDIO, true},
	{ "AUDIO_OFF", AUDIO, false},
	{ "RECORD_ON", RECORD, true},
	{ "RECORD_OFF", RECORD, false},
	{ "PLAY_ON", AUDIO, true},
	{ "PLAY_OFF", AUDIO, false},
	{ "CLOBBER_ON", CLOBBER, true},
	{ "CLOBBER_OFF", CLOBBER, false},
	{ "REPORT_CLIPPING_ON", REPORT_CLIPPING, true},
	{ "REPORT_CLIPPING_OFF", REPORT_CLIPPING, false},
	{ "CHECK_PEAKS_ON", CHECK_PEAKS, true},
	{ "CHECK_PEAKS_OFF", CHECK_PEAKS, false},
	{ "FULL_DUPLEX_ON", FULL_DUPLEX, true},
	{ "FULL_DUPLEX_OFF", FULL_DUPLEX, false},
};
static int _num_params = sizeof(_param_list) / sizeof(Param);

//-------------------------------- _str_to_bool, _str_to_int, _str_to_double ---
static int _str_to_bool(const char *str, bool &val)
{
	if (strcasecmp(str, "yes") == 0 || strcasecmp(str, "true") == 0
			|| strcasecmp(str, "on") == 0 || strcmp(str, "1") == 0)
		val = true;
	else if (strcasecmp(str, "no") == 0 || strcasecmp(str, "false") == 0
			|| strcasecmp(str, "off") == 0 || strcmp(str, "0") == 0)
		val = false;
	else {
		val = false;
		return -1;
	}
	return 0;
}

static int _str_to_int(const char *str, int &val)
{
	char *pos = NULL;
	long num = strtol(str, &pos, 10);
	if (*pos != 0)
		return -1;
	val = (int) num;
	return 0;
}

static int _str_to_double(const char *str, double &val)
{
	char *pos = NULL;
	errno = 0;
	val = strtod(str, &pos);
	if (errno != 0 || (val == 0.0 && pos == str))
		return -1;
	return 0;
}

//--------------------------------------------------------- _set_full_duplex ---
// The full duplex state has now been broken up into the <play> and <record>
// options, used during audio setup.  rtsetparams() checks <record>.
static int _set_full_duplex(const bool full_duplex,
	const bool rtsetparams_called)
{
#ifndef EMBEDDED
	if (full_duplex && rtsetparams_called)
		return die("set_option",
					"Turn on full duplex BEFORE calling rtsetparams.");
#endif

	if (full_duplex)
		Option::record(true);
	else {
		// If not play, then record.
		bool state = Option::record() && !Option::play();
		Option::record(state);
	}
	// Same check as above, for record.
#ifndef EMBEDDED
	if (Option::record() && rtsetparams_called)
		return die("set_option",
					"Turn on record BEFORE calling rtsetparams.");
#endif
	return 0;
}

//---------------------------------------------------- _set_key_value_option ---
static int _set_key_value_option(const char *key, const char *sval,
	const bool rtsetparams_called)
{
	int type = -1;
	for (int j = 0; j < _num_params; j++) {
		if (strcasecmp(_param_list[j].arg, key) == 0) {
			type = _param_list[j].type;
			break;
		}
	}
	if (type == -1)
		return -1;
	if (sval == NULL)
		return die("set_option", "No value for \"%s\"", key);

	int status = 0;
	bool bval = false;
	int ival = 0;
	double dval = 0.0;

	switch (type) {

		// bool options

		case AUDIO:
			status = _str_to_bool(sval, bval);
			Option::audio(bval);
			break;
		case PLAY:
			status = _str_to_bool(sval, bval);
			Option::play(bval);
			break;
		case RECORD:
			status = _str_to_bool(sval, bval);
			Option::record(bval);
#ifndef EMBEDDED
			if (Option::record() && rtsetparams_called)
				return die("set_option",
							"Turn on record BEFORE calling rtsetparams.");
#endif
			break;
		case CLOBBER:
			status = _str_to_bool(sval, bval);
			Option::clobber(bval);
			break;
		case REPORT_CLIPPING:
			status = _str_to_bool(sval, bval);
			Option::reportClipping(bval);
			break;
		case CHECK_PEAKS:
			status = _str_to_bool(sval, bval);
			Option::checkPeaks(bval);
			break;
		case EXIT_ON_ERROR:
			status = _str_to_bool(sval, bval);
			Option::exitOnError(bval);
			break;
		case AUTO_LOAD:
			status = _str_to_bool(sval, bval);
			Option::autoLoad(bval);
			break;
		case FAST_UPDATE:
			status = _str_to_bool(sval, bval);
			Option::fastUpdate(bval);
			if (bval)
				rtcmix_warn("set_option", "With \"%s\" on, certain instruments run "
					"faster at the expense of reduced capabilities.\n", key);
			break;
		case REQUIRE_SAMPLE_RATE:
			status = _str_to_bool(sval, bval);
			Option::requireSampleRate(bval);
			break;

		// number options

		case BUFFER_FRAMES:
			status = _str_to_int(sval, ival);
			if (status == 0) {
				if (ival <= 0)
					return die("set_option", "\"%s\" value must be > 0", key);
				Option::bufferFrames(ival);
			}
			break;
		case BUFFER_COUNT:
			status = _str_to_int(sval, ival);
			if (status == 0) {
				if (ival <= 0)
					return die("set_option", "\"%s\" value must be > 0", key);
				Option::bufferCount(ival);
			}
			break;
		case OSC_INPORT:
			status = _str_to_int(sval, ival);
			if (status == 0) {
				if (ival <= 0)
					return die("set_option", "\"%s\" value must be > 0", key);
				Option::oscInPort(ival);
			}
			break;
		case PRINT:
			status = _str_to_int(sval, ival);
			Option::print(ival);
			break;
		case MUTE_THRESHOLD:
			status = _str_to_double(sval, dval);
			Option::muteThreshold(dval);
			break;

		// string options

		case DEVICE:
			Option::device(sval);
			break;
		case INDEVICE:
			Option::inDevice(sval);
			break;
		case OUTDEVICE:
			Option::outDevice(sval);
			break;
		case MIDI_INDEVICE:
			Option::midiInDevice(sval);
			break;
		case MIDI_OUTDEVICE:
			Option::midiOutDevice(sval);
			break;
		case OSC_HOST:
			Option::oscHost(sval);
			break;
		case DSOPATH:
			Option::dsoPath(sval);
			break;
		case RCNAME:
			Option::rcName(sval);
			break;
		default:
			break;
	}

	if (status == -1)
		return die("set_option", "Trouble parsing value \"%s\"", sval);
	return status;
}

//-------------------------------------------------------- _set_value_option ---
// This way of setting options is deprecated, in favor of the new key=value way.
// NOTE: Do not handle new options in this function; use _set_key_value_option.
static int _set_value_option(const char *sval, const bool rtsetparams_called)
{
	int type = -1;
	bool bval = false;
	for (int j = 0; j < _num_params; j++) {
		if (strcasecmp(_param_list[j].arg, sval) == 0) {
			type = _param_list[j].type;
			bval = _param_list[j].value;
			break;
		}
	}
	if (type == -1)
		return -1;

	switch (type) {
		case AUDIO:
			Option::play(bval);
			break;
		case RECORD:
			Option::record(bval);
#ifndef EMBEDDED
			if (Option::record() && rtsetparams_called)
				return die("set_option",
							"Turn on record BEFORE calling rtsetparams.");
#endif
			break;
		case CLOBBER:
			Option::clobber(bval);
			break;
		case REPORT_CLIPPING:
			Option::reportClipping(bval);
			break;
		case CHECK_PEAKS:
			Option::checkPeaks(bval);
			break;
		case FULL_DUPLEX:
			if (_set_full_duplex(bval, rtsetparams_called) == -1)
				return -1;
			break;
		default:
			break;
	}

	return 0;
}

//--------------------------------------------------------------- _parse_arg ---
static int _parse_arg(const char *arg, const bool rtsetparams_called)
{
	// Strip white-space chars from text to the left of any '=' and between
	// the '=' and the next non-white space.  The reason we don't strip the
	// whole string is to preserve options that must include spaces, such
	// as "MOTU 828".  Store result into <opt>.

	long len = arg ? strlen(arg) : 0;
	if (len > OPT_STRLEN - 1)
		len = OPT_STRLEN - 1;

	char opt[OPT_STRLEN];
	char *p = opt;
	int space_state = 0;
	for (int j = 0; j < len; j++) {
		if (space_state > 1)
			*p++ = arg[j];
		else if (!isspace(arg[j])) {
			if (space_state == 1)
				space_state++;
			else if (arg[j] == '=')
				space_state = 1;
			*p++ = arg[j];
		}
	}
	*p = '\0';

	// Two styles of option string: a single "value" and a "key=value" pair.
	int status = 0;
	p = strchr(opt, '=');				// check for "key=value"
	if (p) {
		*p++ = '\0';						// <opt> is now key only
		if (*p == '\0')					// p now points to value string
			return die("set_option", "Missing value for key \"%s\"", opt);
		status = _set_key_value_option(opt, p, rtsetparams_called);
	}
	else										// check for single "value"
		status = _set_value_option(opt, rtsetparams_called);

	if (status == -1)
		return die("set_option", "Unrecognized option \"%s\"", opt);
	return 0;
}

//--------------------------------------------------------------- set_option ---
double RTcmix::set_option(float *p, int nargs, double pp[])
{
	for (int i = 0; i < nargs; i++) {
		char *arg = DOUBLE_TO_STRING(pp[i]);		// cast pfield to string

		if (_parse_arg(arg, rtsetparams_was_called()) == -1)
			return -1.0;
	}
	return 0.0;
}

<commit_msg>Error handling for set_option now makes some errors terminate the parse.<commit_after>/* RTcmix - Copyright (C) 2005 The RTcmix Development Team
   See ``AUTHORS'' for a list of contributors. See ``LICENSE'' for
   the license to this software and for a DISCLAIMER OF ALL WARRANTIES.
*/
// The set_option function, called from a script, lets the user override
// default options (and those stored in the .rtcmixrc file).  The options
// are kept in the <options> object.  See Option.h for advice on how to add
// new options.
//
// Please keep the items created in the steps above in the same order that they
// appear in Option.h -- makes this file easier to maintain.
//
// -JGG, 6/30/04, rev. 4/10/05

#include <RTcmix.h>
#include <ugens.h>		// for die()
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <ctype.h>
#include <errno.h>
#include <Option.h>

enum ParamType {
	AUDIO,
	PLAY,
	RECORD,
	CLOBBER,
	PRINT,
	MUTE_THRESHOLD,
	REPORT_CLIPPING,
	CHECK_PEAKS,
	FULL_DUPLEX,
	EXIT_ON_ERROR,
    BAIL_ON_ERROR,
	AUTO_LOAD,
	FAST_UPDATE,
	REQUIRE_SAMPLE_RATE,
	BUFFER_FRAMES,
	BUFFER_COUNT,
	OSC_INPORT,
	DEVICE,
	INDEVICE,
	OUTDEVICE,
	MIDI_INDEVICE,
	MIDI_OUTDEVICE,
	OSC_HOST,
	DSOPATH,
	RCNAME
};

#define OPT_STRLEN 128

struct Param {
	char arg[OPT_STRLEN];
	ParamType type;
	bool value;		// use false if not relevant, i.e. for key=value style
};

static Param _param_list[] = {
	// These are key=value option strings.  Please list these in the order in
	// which they appear in Option.h, to make it easier to compare.

	// bool options
	{ kOptionAudio, AUDIO, false},
	{ kOptionPlay, PLAY, false},
	{ kOptionRecord, RECORD, false},
	{ kOptionClobber, CLOBBER, false},
	{ kOptionReportClipping, REPORT_CLIPPING, false},
	{ kOptionCheckPeaks, CHECK_PEAKS, false},
	{ kOptionExitOnError, EXIT_ON_ERROR, false},
    { kOptionBailOnError, BAIL_ON_ERROR, false},
	{ kOptionAutoLoad, AUTO_LOAD, false},
	{ kOptionFastUpdate, FAST_UPDATE, false},
	{ kOptionRequireSampleRate, REQUIRE_SAMPLE_RATE, true},

	// number options
	{ kOptionBufferFrames, BUFFER_FRAMES, false},
	{ kOptionBufferCount, BUFFER_COUNT, false},
	{ kOptionOSCInPort, OSC_INPORT, false},
	{ kOptionPrint, PRINT, false},
	{ kOptionMuteThreshold, MUTE_THRESHOLD, false},

	// string options
	{ kOptionDevice, DEVICE, false},
	{ kOptionInDevice, INDEVICE, false},
	{ kOptionOutDevice, OUTDEVICE, false},
	{ kOptionMidiInDevice, MIDI_INDEVICE, false},
	{ kOptionMidiOutDevice, MIDI_OUTDEVICE, false},
	{ kOptionOSCHost, OSC_HOST, false},
	{ kOptionDSOPath, DSOPATH, false},
	{ kOptionRCName, RCNAME, false},

	// These are the deprecated single-value option strings.
	// Please don't add more.
	{ "AUDIO_ON", AUDIO, true},
	{ "AUDIO_OFF", AUDIO, false},
	{ "RECORD_ON", RECORD, true},
	{ "RECORD_OFF", RECORD, false},
	{ "PLAY_ON", AUDIO, true},
	{ "PLAY_OFF", AUDIO, false},
	{ "CLOBBER_ON", CLOBBER, true},
	{ "CLOBBER_OFF", CLOBBER, false},
	{ "REPORT_CLIPPING_ON", REPORT_CLIPPING, true},
	{ "REPORT_CLIPPING_OFF", REPORT_CLIPPING, false},
	{ "CHECK_PEAKS_ON", CHECK_PEAKS, true},
	{ "CHECK_PEAKS_OFF", CHECK_PEAKS, false},
	{ "FULL_DUPLEX_ON", FULL_DUPLEX, true},
	{ "FULL_DUPLEX_OFF", FULL_DUPLEX, false},
};
static int _num_params = sizeof(_param_list) / sizeof(Param);

//-------------------------------- _str_to_bool, _str_to_int, _str_to_double ---
static int _str_to_bool(const char *str, bool &val)
{
	if (strcasecmp(str, "yes") == 0 || strcasecmp(str, "true") == 0
			|| strcasecmp(str, "on") == 0 || strcmp(str, "1") == 0)
		val = true;
	else if (strcasecmp(str, "no") == 0 || strcasecmp(str, "false") == 0
			|| strcasecmp(str, "off") == 0 || strcmp(str, "0") == 0)
		val = false;
	else {
		val = false;
		return -1;
	}
	return 0;
}

static int _str_to_int(const char *str, int &val)
{
	char *pos = NULL;
	long num = strtol(str, &pos, 10);
	if (*pos != 0)
		return -1;
	val = (int) num;
	return 0;
}

static int _str_to_double(const char *str, double &val)
{
	char *pos = NULL;
	errno = 0;
	val = strtod(str, &pos);
	if (errno != 0 || (val == 0.0 && pos == str))
		return -1;
	return 0;
}

//--------------------------------------------------------- _set_full_duplex ---
// The full duplex state has now been broken up into the <play> and <record>
// options, used during audio setup.  rtsetparams() checks <record>.
static int _set_full_duplex(const bool full_duplex,
	const bool rtsetparams_called)
{
#ifndef EMBEDDED
	if (full_duplex && rtsetparams_called)
		return die("set_option",
					"Turn on full duplex BEFORE calling rtsetparams.");
#endif

	if (full_duplex)
		Option::record(true);
	else {
		// If not play, then record.
		bool state = Option::record() && !Option::play();
		Option::record(state);
	}
	// Same check as above, for record.
#ifndef EMBEDDED
	if (Option::record() && rtsetparams_called)
		return die("set_option",
					"Turn on record BEFORE calling rtsetparams.");
#endif
	return 0;
}

//---------------------------------------------------- _set_key_value_option ---
static int _set_key_value_option(const char *key, const char *sval,
	const bool rtsetparams_called)
{
	int type = -1;
	for (int j = 0; j < _num_params; j++) {
		if (strcasecmp(_param_list[j].arg, key) == 0) {
			type = _param_list[j].type;
			break;
		}
	}
	if (type == -1)
		return -1;
	if (sval == NULL)
		return die("set_option", "No value for \"%s\"", key);

	int status = 0;
	bool bval = false;
	int ival = 0;
	double dval = 0.0;

	switch (type) {

		// bool options

		case AUDIO:
			status = _str_to_bool(sval, bval);
			Option::audio(bval);
			break;
		case PLAY:
			status = _str_to_bool(sval, bval);
			Option::play(bval);
			break;
		case RECORD:
			status = _str_to_bool(sval, bval);
			Option::record(bval);
#ifndef EMBEDDED
			if (Option::record() && rtsetparams_called)
				return die("set_option",
							"Turn on record BEFORE calling rtsetparams.");
#endif
			break;
		case CLOBBER:
			status = _str_to_bool(sval, bval);
			Option::clobber(bval);
			break;
		case REPORT_CLIPPING:
			status = _str_to_bool(sval, bval);
			Option::reportClipping(bval);
			break;
		case CHECK_PEAKS:
			status = _str_to_bool(sval, bval);
			Option::checkPeaks(bval);
			break;
		case EXIT_ON_ERROR:
			status = _str_to_bool(sval, bval);
			Option::exitOnError(bval);
			break;
		case AUTO_LOAD:
			status = _str_to_bool(sval, bval);
			Option::autoLoad(bval);
			break;
		case FAST_UPDATE:
			status = _str_to_bool(sval, bval);
			Option::fastUpdate(bval);
			if (bval)
				rtcmix_warn("set_option", "With \"%s\" on, certain instruments run "
					"faster at the expense of reduced capabilities.\n", key);
			break;
		case REQUIRE_SAMPLE_RATE:
			status = _str_to_bool(sval, bval);
			Option::requireSampleRate(bval);
			break;

		// number options

		case BUFFER_FRAMES:
			status = _str_to_int(sval, ival);
			if (status == 0) {
				if (ival <= 0)
					return die("set_option", "\"%s\" value must be > 0", key);
				Option::bufferFrames(ival);
			}
			break;
		case BUFFER_COUNT:
			status = _str_to_int(sval, ival);
			if (status == 0) {
				if (ival <= 0)
					return die("set_option", "\"%s\" value must be > 0", key);
				Option::bufferCount(ival);
			}
			break;
		case OSC_INPORT:
			status = _str_to_int(sval, ival);
			if (status == 0) {
				if (ival <= 0)
					return die("set_option", "\"%s\" value must be > 0", key);
				Option::oscInPort(ival);
			}
			break;
		case PRINT:
			status = _str_to_int(sval, ival);
			Option::print(ival);
			break;
		case MUTE_THRESHOLD:
			status = _str_to_double(sval, dval);
			Option::muteThreshold(dval);
			break;

		// string options

		case DEVICE:
			Option::device(sval);
			break;
		case INDEVICE:
			Option::inDevice(sval);
			break;
		case OUTDEVICE:
			Option::outDevice(sval);
			break;
		case MIDI_INDEVICE:
			Option::midiInDevice(sval);
			break;
		case MIDI_OUTDEVICE:
			Option::midiOutDevice(sval);
			break;
		case OSC_HOST:
			Option::oscHost(sval);
			break;
		case DSOPATH:
			Option::dsoPath(sval);
			break;
		case RCNAME:
			Option::rcName(sval);
			break;
		default:
			break;
	}

	if (status == -1)
		return die("set_option", "Trouble parsing value \"%s\"", sval);
	return status;
}

//-------------------------------------------------------- _set_value_option ---
// This way of setting options is deprecated, in favor of the new key=value way.
// NOTE: Do not handle new options in this function; use _set_key_value_option.
static int _set_value_option(const char *sval, const bool rtsetparams_called)
{
	int type = -1;
	bool bval = false;
	for (int j = 0; j < _num_params; j++) {
		if (strcasecmp(_param_list[j].arg, sval) == 0) {
			type = _param_list[j].type;
			bval = _param_list[j].value;
			break;
		}
	}
	if (type == -1)
		return -1;

	switch (type) {
		case AUDIO:
			Option::play(bval);
			break;
		case RECORD:
			Option::record(bval);
#ifndef EMBEDDED
            if (Option::record() && rtsetparams_called) {
				die("set_option", "Turn on record BEFORE calling rtsetparams.");
                throw CONFIGURATION_ERROR;
            }
#endif
			break;
		case CLOBBER:
			Option::clobber(bval);
			break;
		case REPORT_CLIPPING:
			Option::reportClipping(bval);
			break;
		case CHECK_PEAKS:
			Option::checkPeaks(bval);
			break;
		case FULL_DUPLEX:
			if (_set_full_duplex(bval, rtsetparams_called) == -1)
				return -1;
			break;
		default:
			break;
	}

	return 0;
}

//--------------------------------------------------------------- _parse_arg ---
static int _parse_arg(const char *arg, const bool rtsetparams_called)
{
	// Strip white-space chars from text to the left of any '=' and between
	// the '=' and the next non-white space.  The reason we don't strip the
	// whole string is to preserve options that must include spaces, such
	// as "MOTU 828".  Store result into <opt>.

	long len = arg ? strlen(arg) : 0;
	if (len > OPT_STRLEN - 1)
		len = OPT_STRLEN - 1;

	char opt[OPT_STRLEN];
	char *p = opt;
	int space_state = 0;
	for (int j = 0; j < len; j++) {
		if (space_state > 1)
			*p++ = arg[j];
		else if (!isspace(arg[j])) {
			if (space_state == 1)
				space_state++;
			else if (arg[j] == '=')
				space_state = 1;
			*p++ = arg[j];
		}
	}
	*p = '\0';

	// Two styles of option string: a single "value" and a "key=value" pair.
	int status = 0;
	p = strchr(opt, '=');				// check for "key=value"
	if (p) {
		*p++ = '\0';						// <opt> is now key only
		if (*p == '\0')					// p now points to value string
			return die("set_option", "Missing value for key \"%s\"", opt);
		status = _set_key_value_option(opt, p, rtsetparams_called);
	}
	else										// check for single "value"
		status = _set_value_option(opt, rtsetparams_called);

    if (status == -1) {
		rtcmix_warn("set_option", "Unrecognized option \"%s\" ignored", opt);
        return rtOptionalThrow(PARAM_ERROR);
    }
	return 0;
}

//--------------------------------------------------------------- set_option ---
double RTcmix::set_option(float *p, int nargs, double pp[])
{
	for (int i = 0; i < nargs; i++) {
		char *arg = DOUBLE_TO_STRING(pp[i]);		// cast pfield to string

		if (_parse_arg(arg, rtsetparams_was_called()) != 0)
			return -1.0;
	}
	return 0.0;
}

<|endoftext|>


<commit_before>#include <sim/constants.h>
#include <sim/db.h>
#include <simlib/logger.h>
#include <simlib/random.h>
#include <simlib/sha.h>

using std::string;
using std::unique_ptr;

static bool DROP_TABLES = false, ONLY_DROP_TABLES = false;

/**
 * @brief Displays help
 */
static void help(const char* program_name) {
	if (program_name == nullptr)
		program_name = "setup-installation";

	printf("Usage: %s [options] INSTALL_DIR", program_name);
	puts("Setup database after SIM installation");
	puts("");
	puts("Options:");
	puts("  --drop-tables          Drop database tables before recreating them");
	puts("  -h, --help             Display this information");
	puts("  --only-drop-tables     Drop database tables and exit");
}

static void parseOptions(int &argc, char **argv) {
	int new_argc = 1;
	for (int i = 1; i < argc; ++i) {

		if (argv[i][0] == '-') {
			// Drop tables
			if (0 == strcmp(argv[i], "--drop-tables"))
				DROP_TABLES = true;

			// Help
			else if (0 == strcmp(argv[i], "-h") ||
				0 == strcmp(argv[i], "--help"))
			{
				help(argv[0]); // argv[0] is valid (argc > 1)
				exit(0);
			}

			// Drop tables
			else if (0 == strcmp(argv[i], "--only-drop-tables")) {
				DROP_TABLES = true;
				ONLY_DROP_TABLES = true;
			}

			// Unknown
			else
				errlog("Unknown option: '", argv[i], '\'');

		} else
			argv[new_argc++] = argv[i];
	}

	argc = new_argc;
}

struct TryCreateTable {
	bool error = false;
	DB::Connection& conn_;

	explicit TryCreateTable(DB::Connection& conn) : conn_(conn) {}

	template<class Func>
	void operator()(const char* table_name, const std::string& query, Func&& f)
		noexcept
	{
		try {
			conn_.executeUpdate(query);
			f();
		} catch (const std::exception& e) {
			errlog("\033[31mFailed to create table `", table_name, "`\033[m - ",
				e.what());
			error = true;
		}
	}

	void operator()(const char* table_name, const string& query) noexcept {
		operator()(table_name, query, []{});
	}
};

int main(int argc, char **argv) {
	errlog.label(false);

	parseOptions(argc, argv);

	if(argc != 2) {
		help(argc > 0 ? argv[0] : nullptr);
		return 1;
	}

	DB::Connection conn;
	try {
		// Get connection
		conn = DB::createConnectionUsingPassFile(
			concat(argv[1], "/.db.config"));

	} catch (const std::exception& e) {
		errlog("\033[31mFailed to connect to database\033[m - ", e.what());
		return 4;
	}

	if (DROP_TABLES) {
		try {
			conn.executeUpdate("DROP TABLE iF EXISTS users");
			conn.executeUpdate("DROP TABLE iF EXISTS session");
			conn.executeUpdate("DROP TABLE iF EXISTS problems");
			conn.executeUpdate("DROP TABLE iF EXISTS rounds");
			conn.executeUpdate("DROP TABLE iF EXISTS contests_users");
			conn.executeUpdate("DROP TABLE iF EXISTS submissions");
			conn.executeUpdate("DROP TABLE iF EXISTS files");

			if (ONLY_DROP_TABLES)
				return 0;

		} catch (const std::exception& e) {
			errlog("\033[31mFailed to drop tables\033[m - ", e.what());
			return 5;
		}
	}

	TryCreateTable tryCreateTable(conn);

	tryCreateTable("users",
		concat("CREATE TABLE IF NOT EXISTS `users` ("
			"`id` int unsigned NOT NULL AUTO_INCREMENT,"
			"`username` varchar(", toStr(USERNAME_MAX_LEN), ") NOT NULL,"
			"`first_name` varchar(", toStr(USER_FIRST_NAME_MAX_LEN), ") NOT NULL,"
			"`last_name` varchar(", toStr(USER_LAST_NAME_MAX_LEN), ") NOT NULL,"
			"`email` varchar(", toStr(USER_EMAIL_MAX_LEN), ") NOT NULL,"
			"`salt` char(", toStr(SALT_LEN), ") NOT NULL,"
			"`password` char(", toStr(PASSWORD_HASH_LEN), ") NOT NULL,"
			"`type` tinyint(1) unsigned NOT NULL DEFAULT " UTYPE_NORMAL_STR ","
			"PRIMARY KEY (`id`),"
			"UNIQUE KEY `username` (`username`)"
		") ENGINE=MyISAM AUTO_INCREMENT=1 DEFAULT CHARSET=utf8 COLLATE=utf8_bin"),
		[&] {
			// Add default user sim with password sim
			char salt_bin[SALT_LEN >> 1];
			fillRandomly(salt_bin, sizeof(salt_bin));
			string salt = toHex(salt_bin, sizeof(salt_bin));

			DB::Statement stmt(conn.prepare(
				"INSERT IGNORE users (id, username, first_name, last_name, email, "
					"salt, password, type) "
				"VALUES (" SIM_ROOT_UID ", 'sim', 'sim', 'sim', 'sim@sim', ?, "
					"?, 0)"));
			stmt.setString(1, salt);
			stmt.setString(2, sha3_512(salt + "sim"));
			stmt.executeUpdate();
		});

	tryCreateTable("session",
		concat("CREATE TABLE IF NOT EXISTS `session` ("
			"`id` char(", toStr(SESSION_ID_LEN), ") NOT NULL,"
			"`csrf_token` char(", toStr(SESSION_CSRF_TOKEN_LEN), ") NOT NULL,"
			"`user_id` int unsigned NOT NULL,"
			"`data` blob NOT NULL,"
			"`ip` char(", toStr(SESSION_IP_LEN), ") NOT NULL,"
			"`user_agent` blob NOT NULL,"
			"`time` datetime NOT NULL,"
			"PRIMARY KEY (`id`),"
			"KEY (`user_id`),"
			"KEY (`time`)"
		") ENGINE=MyISAM DEFAULT CHARSET=utf8 COLLATE=utf8_bin;"));

	tryCreateTable("problems",
		concat("CREATE TABLE IF NOT EXISTS `problems` ("
			"`id` int unsigned NOT NULL AUTO_INCREMENT,"
			"`is_public` BOOLEAN NOT NULL DEFAULT FALSE,"
			"`name` VARCHAR(", toStr(PROBLEM_NAME_MAX_LEN), ") NOT NULL,"
			"`tag` VARCHAR(", toStr(PROBLEM_TAG_MAX_LEN), ") NOT NULL,"
			"`owner` int unsigned NOT NULL,"
			"`added` datetime NOT NULL,"
			"PRIMARY KEY (`id`),"
			"KEY (`owner`)"
		") ENGINE=MyISAM AUTO_INCREMENT=1 DEFAULT CHARSET=utf8 COLLATE=utf8_bin"));

	tryCreateTable("rounds",
		concat("CREATE TABLE IF NOT EXISTS `rounds` ("
			"`id` int unsigned NOT NULL AUTO_INCREMENT,"
			"`parent` int unsigned NULL DEFAULT NULL,"
			"`grandparent` int unsigned NULL DEFAULT NULL,"
			"`problem_id` int unsigned DEFAULT NULL,"
			"`name` VARCHAR(", toStr(ROUND_NAME_MAX_LEN), ") NOT NULL,"
			"`owner` int unsigned NOT NULL,"
			"`item` int unsigned NOT NULL,"
			"`is_public` BOOLEAN NOT NULL DEFAULT FALSE,"
			"`visible` BOOLEAN NOT NULL DEFAULT FALSE,"
			"`show_ranking` BOOLEAN NOT NULL DEFAULT FALSE,"
			"`begins` datetime NULL DEFAULT NULL,"
			"`full_results` datetime NULL DEFAULT NULL,"
			"`ends` datetime NULL DEFAULT NULL,"
			"PRIMARY KEY (`id`),"
			"KEY (`parent`, `visible`),"
			"KEY (`parent`, `begins`),"
			"KEY (`parent`, `is_public`),"
			"KEY (`grandparent`, `item`),"
			"KEY (`owner`)"
		") ENGINE=MyISAM AUTO_INCREMENT=1 DEFAULT CHARSET=utf8 COLLATE=utf8_bin"));

	tryCreateTable("contests_users",
		"CREATE TABLE IF NOT EXISTS `contests_users` ("
			"`user_id` int unsigned NOT NULL,"
			"`contest_id` int unsigned NOT NULL,"
			"`mode` tinyint(1) unsigned NOT NULL DEFAULT " CU_MODE_CONTESTANT_STR ","
			"PRIMARY KEY (`user_id`, `contest_id`),"
			"KEY (`contest_id`)"
		") ENGINE=MyISAM DEFAULT CHARSET=utf8 COLLATE=utf8_bin");

	tryCreateTable("submissions",
		"CREATE TABLE IF NOT EXISTS `submissions` ("
			"`id` int unsigned NOT NULL AUTO_INCREMENT,"
			"`user_id` int unsigned NOT NULL,"
			"`problem_id` int unsigned NOT NULL,"
			"`round_id` int unsigned NOT NULL,"
			"`parent_round_id` int unsigned NOT NULL,"
			"`contest_round_id` int unsigned NOT NULL,"
			"`type` TINYINT NULL DEFAULT NULL,"
			"`status` TINYINT NOT NULL DEFAULT 0,"
			"`submit_time` datetime NOT NULL,"
			"`score` int NULL DEFAULT NULL,"
			"`queued` datetime NOT NULL,"
			"`initial_report` blob NOT NULL,"
			"`final_report` blob NOT NULL,"
			"PRIMARY KEY (id),"
			// Judge server
			"KEY (status, queued),"
			// Update type, delete account
			"KEY (user_id, round_id, type, status, id),"
			// Contest::submissions() - view all
			"KEY (round_id, id),"
			"KEY (round_id, user_id, id),"
			"KEY (parent_round_id, id),"
			"KEY (parent_round_id, user_id, id),"
			"KEY (contest_round_id, id),"
			"KEY (contest_round_id, user_id, id),"
			// Contest::submissions() - view by type
			"KEY (round_id, type, id),"
			"KEY (round_id, user_id, type, id),"
			"KEY (parent_round_id, type, id),"
			"KEY (parent_round_id, user_id, type, id),"
			"KEY (contest_round_id, type, id),"
			"KEY (contest_round_id, user_id, type, id)"
		") ENGINE=MyISAM AUTO_INCREMENT=1 DEFAULT CHARSET=utf8 COLLATE=utf8_bin");

	tryCreateTable("files",
		concat("CREATE TABLE IF NOT EXISTS `files` ("
			"`id` char(", toStr(FILE_ID_LEN), ") NOT NULL,"
			"`round_id` int unsigned NULL,"
			"`name` VARCHAR(", toStr(FILE_NAME_MAX_LEN), ") NOT NULL,"
			"`description` VARCHAR(", toStr(FILE_DESCRIPTION_MAX_LEN), ") "
				"NOT NULL,"
			"`file_size` bigint unsigned NOT NULL,"
			"`modified` datetime NOT NULL,"
			"PRIMARY KEY (id),"
			"KEY (round_id, modified)"
		") ENGINE=MyISAM DEFAULT CHARSET=utf8 COLLATE=utf8_bin"));

	if (tryCreateTable.error)
		return 6;

	return 0;
}
<commit_msg>Added some validation during database tables creation<commit_after>#include <sim/constants.h>
#include <sim/db.h>
#include <simlib/random.h>
#include <simlib/sha.h>
#include <simlib/utilities.h>

using std::array;
using std::string;
using std::unique_ptr;

static bool DROP_TABLES = false, ONLY_DROP_TABLES = false;

/**
 * @brief Displays help
 */
static void help(const char* program_name) {
	if (program_name == nullptr)
		program_name = "setup-installation";

	printf("Usage: %s [options] INSTALL_DIR", program_name);
	puts("Setup database after SIM installation");
	puts("");
	puts("Options:");
	puts("  --drop-tables          Drop database tables before recreating them");
	puts("  -h, --help             Display this information");
	puts("  --only-drop-tables     Drop database tables and exit");
}

static void parseOptions(int &argc, char **argv) {
	int new_argc = 1;
	for (int i = 1; i < argc; ++i) {

		if (argv[i][0] == '-') {
			// Drop tables
			if (0 == strcmp(argv[i], "--drop-tables"))
				DROP_TABLES = true;

			// Help
			else if (0 == strcmp(argv[i], "-h") ||
				0 == strcmp(argv[i], "--help"))
			{
				help(argv[0]); // argv[0] is valid (argc > 1)
				exit(0);
			}

			// Drop tables
			else if (0 == strcmp(argv[i], "--only-drop-tables")) {
				DROP_TABLES = true;
				ONLY_DROP_TABLES = true;
			}

			// Unknown
			else
				errlog("Unknown option: '", argv[i], '\'');

		} else
			argv[new_argc++] = argv[i];
	}

	argc = new_argc;
}

constexpr array<meta::string, 7> tables {{
	"contests_users",
	"files",
	"problems",
	"rounds",
	"session",
	"submissions",
	"users",
}};

static_assert(meta::is_sorted(tables), "Needed for binary search");

struct TryToCreateTable {
	bool error = false;
	DB::Connection& conn_;

	explicit TryToCreateTable(DB::Connection& conn) : conn_(conn) {}

	template<class Func>
	void operator()(const char* table_name, const std::string& query, Func&& f)
		noexcept
	{
		try {
			if (!binary_search(tables, StringView{table_name}))
				THROW("Table `", table_name, "` not found in the table list");

			conn_.executeUpdate(query);
			f();

		} catch (const std::exception& e) {
			errlog("\033[31mFailed to create table `", table_name, "`\033[m - ",
				e.what());
			error = true;
		}
	}

	void operator()(const char* table_name, const string& query) noexcept {
		operator()(table_name, query, []{});
	}
};

int main(int argc, char **argv) {
	errlog.label(false);

	parseOptions(argc, argv);

	if(argc != 2) {
		help(argc > 0 ? argv[0] : nullptr);
		return 1;
	}

	DB::Connection conn;
	try {
		// Get connection
		conn = DB::createConnectionUsingPassFile(
			concat(argv[1], "/.db.config"));

	} catch (const std::exception& e) {
		errlog("\033[31mFailed to connect to database\033[m - ", e.what());
		return 4;
	}

	if (DROP_TABLES) {
		try {
			conn.executeUpdate("DROP TABLE iF EXISTS users");
			conn.executeUpdate("DROP TABLE iF EXISTS session");
			conn.executeUpdate("DROP TABLE iF EXISTS problems");
			conn.executeUpdate("DROP TABLE iF EXISTS rounds");
			conn.executeUpdate("DROP TABLE iF EXISTS contests_users");
			conn.executeUpdate("DROP TABLE iF EXISTS submissions");
			conn.executeUpdate("DROP TABLE iF EXISTS files");

			if (ONLY_DROP_TABLES)
				return 0;

		} catch (const std::exception& e) {
			errlog("\033[31mFailed to drop tables\033[m - ", e.what());
			return 5;
		}
	}

	TryToCreateTable try_to_create_table(conn);

	try_to_create_table("users",
		concat("CREATE TABLE IF NOT EXISTS `users` ("
			"`id` int unsigned NOT NULL AUTO_INCREMENT,"
			"`username` varchar(", toStr(USERNAME_MAX_LEN), ") NOT NULL,"
			"`first_name` varchar(", toStr(USER_FIRST_NAME_MAX_LEN), ") NOT NULL,"
			"`last_name` varchar(", toStr(USER_LAST_NAME_MAX_LEN), ") NOT NULL,"
			"`email` varchar(", toStr(USER_EMAIL_MAX_LEN), ") NOT NULL,"
			"`salt` char(", toStr(SALT_LEN), ") NOT NULL,"
			"`password` char(", toStr(PASSWORD_HASH_LEN), ") NOT NULL,"
			"`type` tinyint(1) unsigned NOT NULL DEFAULT " UTYPE_NORMAL_STR ","
			"PRIMARY KEY (`id`),"
			"UNIQUE KEY `username` (`username`)"
		") ENGINE=MyISAM AUTO_INCREMENT=1 DEFAULT CHARSET=utf8 COLLATE=utf8_bin"),
		[&] {
			// Add default user sim with password sim
			char salt_bin[SALT_LEN >> 1];
			fillRandomly(salt_bin, sizeof(salt_bin));
			string salt = toHex(salt_bin, sizeof(salt_bin));

			DB::Statement stmt(conn.prepare(
				"INSERT IGNORE users (id, username, first_name, last_name, email, "
					"salt, password, type) "
				"VALUES (" SIM_ROOT_UID ", 'sim', 'sim', 'sim', 'sim@sim', ?, "
					"?, 0)"));
			stmt.setString(1, salt);
			stmt.setString(2, sha3_512(salt + "sim"));
			stmt.executeUpdate();
		});

	try_to_create_table("session",
		concat("CREATE TABLE IF NOT EXISTS `session` ("
			"`id` char(", toStr(SESSION_ID_LEN), ") NOT NULL,"
			"`csrf_token` char(", toStr(SESSION_CSRF_TOKEN_LEN), ") NOT NULL,"
			"`user_id` int unsigned NOT NULL,"
			"`data` blob NOT NULL,"
			"`ip` char(", toStr(SESSION_IP_LEN), ") NOT NULL,"
			"`user_agent` blob NOT NULL,"
			"`time` datetime NOT NULL,"
			"PRIMARY KEY (`id`),"
			"KEY (`user_id`),"
			"KEY (`time`)"
		") ENGINE=MyISAM DEFAULT CHARSET=utf8 COLLATE=utf8_bin;"));

	try_to_create_table("problems",
		concat("CREATE TABLE IF NOT EXISTS `problems` ("
			"`id` int unsigned NOT NULL AUTO_INCREMENT,"
			"`is_public` BOOLEAN NOT NULL DEFAULT FALSE,"
			"`name` VARCHAR(", toStr(PROBLEM_NAME_MAX_LEN), ") NOT NULL,"
			"`tag` VARCHAR(", toStr(PROBLEM_TAG_MAX_LEN), ") NOT NULL,"
			"`owner` int unsigned NOT NULL,"
			"`added` datetime NOT NULL,"
			"PRIMARY KEY (`id`),"
			"KEY (`owner`)"
		") ENGINE=MyISAM AUTO_INCREMENT=1 DEFAULT CHARSET=utf8 COLLATE=utf8_bin"));

	try_to_create_table("rounds",
		concat("CREATE TABLE IF NOT EXISTS `rounds` ("
			"`id` int unsigned NOT NULL AUTO_INCREMENT,"
			"`parent` int unsigned NULL DEFAULT NULL,"
			"`grandparent` int unsigned NULL DEFAULT NULL,"
			"`problem_id` int unsigned DEFAULT NULL,"
			"`name` VARCHAR(", toStr(ROUND_NAME_MAX_LEN), ") NOT NULL,"
			"`owner` int unsigned NOT NULL,"
			"`item` int unsigned NOT NULL,"
			"`is_public` BOOLEAN NOT NULL DEFAULT FALSE,"
			"`visible` BOOLEAN NOT NULL DEFAULT FALSE,"
			"`show_ranking` BOOLEAN NOT NULL DEFAULT FALSE,"
			"`begins` datetime NULL DEFAULT NULL,"
			"`full_results` datetime NULL DEFAULT NULL,"
			"`ends` datetime NULL DEFAULT NULL,"
			"PRIMARY KEY (`id`),"
			"KEY (`parent`, `visible`),"
			"KEY (`parent`, `begins`),"
			"KEY (`parent`, `is_public`),"
			"KEY (`grandparent`, `item`),"
			"KEY (`owner`)"
		") ENGINE=MyISAM AUTO_INCREMENT=1 DEFAULT CHARSET=utf8 COLLATE=utf8_bin"));

	try_to_create_table("contests_users",
		"CREATE TABLE IF NOT EXISTS `contests_users` ("
			"`user_id` int unsigned NOT NULL,"
			"`contest_id` int unsigned NOT NULL,"
			"`mode` tinyint(1) unsigned NOT NULL DEFAULT " CU_MODE_CONTESTANT_STR ","
			"PRIMARY KEY (`user_id`, `contest_id`),"
			"KEY (`contest_id`)"
		") ENGINE=MyISAM DEFAULT CHARSET=utf8 COLLATE=utf8_bin");

	try_to_create_table("submissions",
		"CREATE TABLE IF NOT EXISTS `submissions` ("
			"`id` int unsigned NOT NULL AUTO_INCREMENT,"
			"`user_id` int unsigned NOT NULL,"
			"`problem_id` int unsigned NOT NULL,"
			"`round_id` int unsigned NOT NULL,"
			"`parent_round_id` int unsigned NOT NULL,"
			"`contest_round_id` int unsigned NOT NULL,"
			"`type` TINYINT NULL DEFAULT NULL,"
			"`status` TINYINT NOT NULL DEFAULT 0,"
			"`submit_time` datetime NOT NULL,"
			"`score` int NULL DEFAULT NULL,"
			"`queued` datetime NOT NULL,"
			"`initial_report` blob NOT NULL,"
			"`final_report` blob NOT NULL,"
			"PRIMARY KEY (id),"
			// Judge server
			"KEY (status, queued),"
			// Update type, delete account
			"KEY (user_id, round_id, type, status, id),"
			// Contest::submissions() - view all
			"KEY (round_id, id),"
			"KEY (round_id, user_id, id),"
			"KEY (parent_round_id, id),"
			"KEY (parent_round_id, user_id, id),"
			"KEY (contest_round_id, id),"
			"KEY (contest_round_id, user_id, id),"
			// Contest::submissions() - view by type
			"KEY (round_id, type, id),"
			"KEY (round_id, user_id, type, id),"
			"KEY (parent_round_id, type, id),"
			"KEY (parent_round_id, user_id, type, id),"
			"KEY (contest_round_id, type, id),"
			"KEY (contest_round_id, user_id, type, id)"
		") ENGINE=MyISAM AUTO_INCREMENT=1 DEFAULT CHARSET=utf8 COLLATE=utf8_bin");

	try_to_create_table("files",
		concat("CREATE TABLE IF NOT EXISTS `files` ("
			"`id` char(", toStr(FILE_ID_LEN), ") NOT NULL,"
			"`round_id` int unsigned NULL,"
			"`name` VARCHAR(", toStr(FILE_NAME_MAX_LEN), ") NOT NULL,"
			"`description` VARCHAR(", toStr(FILE_DESCRIPTION_MAX_LEN), ") "
				"NOT NULL,"
			"`file_size` bigint unsigned NOT NULL,"
			"`modified` datetime NOT NULL,"
			"PRIMARY KEY (id),"
			"KEY (round_id, modified)"
		") ENGINE=MyISAM DEFAULT CHARSET=utf8 COLLATE=utf8_bin"));

	if (try_to_create_table.error)
		return 6;

	return 0;
}
<|endoftext|>


<commit_before>/*
 * MegaMol
 * Copyright (c) 2021, MegaMol Dev Team
 * All rights reserved.
 */

#include "MMFTDataSource.h"

#include "mmcore/CoreInstance.h"
#include "mmcore/param/ButtonParam.h"
#include "mmcore/param/FilePathParam.h"

#include "vislib/String.h"
#include "vislib/sys/FastFile.h"

using namespace megamol::stdplugin::datatools;
using namespace megamol::stdplugin::datatools::table;
using namespace megamol;

MMFTDataSource::MMFTDataSource()
        : core::Module()
        , getDataSlot_("getData", "Slot providing the data")
        , filenameSlot_("filename", "The file name")
        , reloadSlot_("reload", "Reload file")
        , dataHash_(0)
        , reload_(false)
        , columns_()
        , values_() {

    filenameSlot_ << new core::param::FilePathParam("");
    MakeSlotAvailable(&filenameSlot_);
    reloadSlot_ << new core::param::ButtonParam();
    reloadSlot_.SetUpdateCallback(this, &MMFTDataSource::reloadCallback);
    MakeSlotAvailable(&reloadSlot_);

    getDataSlot_.SetCallback(TableDataCall::ClassName(), "GetData", &MMFTDataSource::getDataCallback);
    getDataSlot_.SetCallback(TableDataCall::ClassName(), "GetHash", &MMFTDataSource::getHashCallback);
    MakeSlotAvailable(&getDataSlot_);
}

MMFTDataSource::~MMFTDataSource() {
    Release();
}

bool MMFTDataSource::create() {
    // nothing to do
    return true;
}

void MMFTDataSource::release() {
    columns_.clear();
    values_.clear();
}

void MMFTDataSource::assertData(void) {
    if (!filenameSlot_.IsDirty() && !reload_) {
        return; // nothing to do
    }

    filenameSlot_.ResetDirty();
    reload_ = false;

    columns_.clear();
    values_.clear();

    vislib::sys::FastFile file;
    if (!file.Open(filenameSlot_.Param<core::param::FilePathParam>()->Value(), vislib::sys::File::READ_ONLY,
            vislib::sys::File::SHARE_READ, vislib::sys::File::OPEN_ONLY)) {
        megamol::core::utility::log::Log::DefaultLog.WriteError("Unable to open file \"%s\". Abort.",
            vislib::StringA(filenameSlot_.Param<core::param::FilePathParam>()->Value()).PeekBuffer());
        return;
    }

#define ABORT_ERROR(...)                                                      \
    {                                                                         \
        megamol::core::utility::log::Log::DefaultLog.WriteError(__VA_ARGS__); \
        file.Close();                                                         \
        columns_.clear();                                                     \
        values_.clear();                                                      \
        return;                                                               \
    }
#define ASSERT_READ(A, S)           \
    if (file.Read((A), (S)) != (S)) \
    ABORT_ERROR("Read error %d", __LINE__)

    vislib::StringA magicID;
    ASSERT_READ(magicID.AllocateBuffer(6), 6);
    if (!magicID.Equals("MMFTD"))
        ABORT_ERROR("Wrong file format magic ID");
    uint16_t version;
    ASSERT_READ(&version, 2);
    if (version != 0)
        ABORT_ERROR("Wrong file format version number");

    uint32_t colCnt;
    ASSERT_READ(&colCnt, 4);
    columns_.resize(colCnt);

    for (uint32_t c = 0; c < colCnt; ++c) {
        TableDataCall::ColumnInfo& ci = columns_[c];
        uint16_t nameLen;
        ASSERT_READ(&nameLen, 2);
        vislib::StringA name;
        ASSERT_READ(name.AllocateBuffer(nameLen), nameLen);
        ci.SetName(name.PeekBuffer());
        uint8_t type;
        ASSERT_READ(&type, 1);
        ci.SetType((type == 1) ? TableDataCall::ColumnType::CATEGORICAL : TableDataCall::ColumnType::QUANTITATIVE);
        float f;
        ASSERT_READ(&f, 4);
        ci.SetMinimumValue(f);
        ASSERT_READ(&f, 4);
        ci.SetMaximumValue(f);
    }

    uint64_t rowCnt;
    ASSERT_READ(&rowCnt, 8);

    values_.resize(static_cast<std::vector<float>::size_type>(rowCnt * colCnt));

    ASSERT_READ(values_.data(), rowCnt * colCnt * 4);

    dataHash_++;
}

bool MMFTDataSource::getDataCallback(core::Call& caller) {
    TableDataCall* tfd = dynamic_cast<TableDataCall*>(&caller);
    if (tfd == nullptr)
        return false;

    assertData();

    tfd->SetDataHash(dataHash_);
    tfd->SetFrameCount(1);
    if (values_.size() == 0) {
        tfd->Set(0, 0, nullptr, nullptr);
    } else {
        assert((values.size() % columns.size()) == 0);
        tfd->Set(columns_.size(), values_.size() / columns_.size(), columns_.data(), values_.data());
    }
    tfd->SetUnlocker(nullptr);

    return true;
}

bool MMFTDataSource::getHashCallback(core::Call& caller) {
    TableDataCall* tfd = dynamic_cast<TableDataCall*>(&caller);
    if (tfd == nullptr)
        return false;

    assertData();

    tfd->SetFrameCount(1);
    tfd->SetDataHash(dataHash_);
    tfd->SetUnlocker(nullptr);

    return true;
}

bool MMFTDataSource::reloadCallback(core::param::ParamSlot& caller) {
    reload_ = true;
    return true;
}
<commit_msg>replace vislib<commit_after>/*
 * MegaMol
 * Copyright (c) 2021, MegaMol Dev Team
 * All rights reserved.
 */

#include "MMFTDataSource.h"

#include "mmcore/CoreInstance.h"
#include "mmcore/param/ButtonParam.h"
#include "mmcore/param/FilePathParam.h"

using namespace megamol::stdplugin::datatools::table;
using namespace megamol;

namespace {
template<typename T>
T read(std::istream& stream) {
    T value;
    stream.read(reinterpret_cast<char*>(&value), sizeof(T));
    if (!stream.good()) {
        throw std::runtime_error("Error reading from stream!");
    }
    return value;
}

template<typename T>
std::vector<T> read_vector(std::istream& stream, std::size_t size) {
    std::vector<T> vec(size);
    stream.read(reinterpret_cast<char*>(vec.data()), size * sizeof(T));
    if (!stream.good()) {
        throw std::runtime_error("Error reading from stream!");
    }
    return vec;
}

std::string read_string(std::istream& stream, std::size_t size) {
    std::string str(size, '\0');
    stream.read(str.data(), size * sizeof(std::string::value_type));
    if (!stream.good()) {
        throw std::runtime_error("Error reading from stream!");
    }
    str.erase(std::find(str.begin(), str.end(), '\0'), str.end());
    return str;
}

} // namespace

MMFTDataSource::MMFTDataSource()
        : core::Module()
        , getDataSlot_("getData", "Slot providing the data")
        , filenameSlot_("filename", "The file name")
        , reloadSlot_("reload", "Reload file")
        , dataHash_(0)
        , reload_(false)
        , columns_()
        , values_() {

    filenameSlot_ << new core::param::FilePathParam("");
    MakeSlotAvailable(&filenameSlot_);
    reloadSlot_ << new core::param::ButtonParam();
    reloadSlot_.SetUpdateCallback(this, &MMFTDataSource::reloadCallback);
    MakeSlotAvailable(&reloadSlot_);

    getDataSlot_.SetCallback(TableDataCall::ClassName(), "GetData", &MMFTDataSource::getDataCallback);
    getDataSlot_.SetCallback(TableDataCall::ClassName(), "GetHash", &MMFTDataSource::getHashCallback);
    MakeSlotAvailable(&getDataSlot_);
}

MMFTDataSource::~MMFTDataSource() {
    Release();
}

bool MMFTDataSource::create() {
    // nothing to do
    return true;
}

void MMFTDataSource::release() {
    columns_.clear();
    values_.clear();
}

void MMFTDataSource::assertData(void) {
    if (!filenameSlot_.IsDirty() && !reload_) {
        return; // nothing to do
    }

    filenameSlot_.ResetDirty();
    reload_ = false;

    columns_.clear();
    values_.clear();

    std::string filename(filenameSlot_.Param<core::param::FilePathParam>()->Value().PeekBuffer());
    std::ifstream file(filename, std::ios::binary);
    if (!file.is_open()) {
        megamol::core::utility::log::Log::DefaultLog.WriteError("Unable to open file \"%s\". Abort.", filename.c_str());
        return;
    }

    try {
        std::string magicId = read_string(file, 6);
        if (magicId != "MMFTD") {
            throw std::runtime_error("Wrong file format magic ID!");
        }

        auto version = read<uint16_t>(file);
        if (version != 0) {
            throw std::runtime_error("Wrong file format version number");
        }

        auto colCount = read<uint32_t>(file);
        columns_.resize(colCount);

        for (uint32_t c = 0; c < colCount; ++c) {
            TableDataCall::ColumnInfo& ci = columns_[c];
            auto nameLen = read<uint16_t>(file);
            ci.SetName(read_string(file, nameLen));
            auto type = read<uint8_t>(file);
            ci.SetType((type == 1) ? TableDataCall::ColumnType::CATEGORICAL : TableDataCall::ColumnType::QUANTITATIVE);
            ci.SetMinimumValue(read<float>(file));
            ci.SetMaximumValue(read<float>(file));
        }

        auto rowCount = read<uint64_t>(file);

        values_ = read_vector<float>(file, rowCount * colCount);

        dataHash_++;

    } catch (std::exception& ex) {
        megamol::core::utility::log::Log::DefaultLog.WriteError(ex.what());
        columns_.clear();
        values_.clear();
        return;
    }
}

bool MMFTDataSource::getDataCallback(core::Call& caller) {
    TableDataCall* tfd = dynamic_cast<TableDataCall*>(&caller);
    if (tfd == nullptr) {
        return false;
    }

    assertData();

    tfd->SetDataHash(dataHash_);
    tfd->SetFrameCount(1);
    if (values_.empty()) {
        tfd->Set(0, 0, nullptr, nullptr);
    } else {
        assert((values_.size() % columns_.size()) == 0);
        tfd->Set(columns_.size(), values_.size() / columns_.size(), columns_.data(), values_.data());
    }
    tfd->SetUnlocker(nullptr);

    return true;
}

bool MMFTDataSource::getHashCallback(core::Call& caller) {
    TableDataCall* tfd = dynamic_cast<TableDataCall*>(&caller);
    if (tfd == nullptr) {
        return false;
    }

    assertData();

    tfd->SetFrameCount(1);
    tfd->SetDataHash(dataHash_);
    tfd->SetUnlocker(nullptr);

    return true;
}

bool MMFTDataSource::reloadCallback(core::param::ParamSlot& caller) {
    reload_ = true;
    return true;
}
<|endoftext|>


<commit_before>/*************************************************************************
 *
 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
 *
 * Copyright 2000, 2010 Oracle and/or its affiliates.
 *
 * OpenOffice.org - a multi-platform office productivity suite
 *
 * This file is part of OpenOffice.org.
 *
 * OpenOffice.org is free software: you can redistribute it and/or modify
 * it under the terms of the GNU Lesser General Public License version 3
 * only, as published by the Free Software Foundation.
 *
 * OpenOffice.org is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU Lesser General Public License version 3 for more details
 * (a copy is included in the LICENSE file that accompanied this code).
 *
 * You should have received a copy of the GNU Lesser General Public License
 * version 3 along with OpenOffice.org.  If not, see
 * <http://www.openoffice.org/license.html>
 * for a copy of the LGPLv3 License.
 *
 ************************************************************************/
#include <BorderHandler.hxx>
#include <PropertyMap.hxx>
#include <resourcemodel/QNameToString.hxx>
#include <doctok/resourceids.hxx>
#include <ConversionHelper.hxx>
#include <com/sun/star/table/BorderLine2.hpp>
#include <ooxml/resourceids.hxx>
#include <dmapperLoggers.hxx>

namespace writerfilter {

namespace dmapper {

using namespace ::com::sun::star;
//using namespace ::std;

/*-- 24.04.2007 09:06:35---------------------------------------------------

  -----------------------------------------------------------------------*/
BorderHandler::BorderHandler( bool bOOXML ) :
    m_nCurrentBorderPosition( BORDER_TOP ),
    m_nLineWidth(0),
    m_nLineType(0),
    m_nLineColor(0),
    m_nLineDistance(0),
    m_bOOXML( bOOXML )
{
    const int nBorderCount(BORDER_COUNT);
    std::fill_n(m_aFilledLines, nBorderCount, false);
    std::fill_n(m_aBorderLines, nBorderCount, table::BorderLine());
}
/*-- 24.04.2007 09:06:35---------------------------------------------------

  -----------------------------------------------------------------------*/
BorderHandler::~BorderHandler()
{
}
/*-- 24.04.2007 09:06:35---------------------------------------------------

  -----------------------------------------------------------------------*/
void BorderHandler::attribute(Id rName, Value & rVal)
{
#ifdef DEBUG_DOMAINMAPPER
    dmapper_logger->startElement("BorderHandler.attribute");
    dmapper_logger->attribute("id", (*QNameToString::Instance())(rName));
    dmapper_logger->endElement("BorderHandler.attribute");
#endif

    sal_Int32 nIntValue = rVal.getInt();
    /* WRITERFILTERSTATUS: table: BorderHandler_attributedata */
    switch( rName )
    {
        /* WRITERFILTERSTATUS: done: 75, planned: 0, spent: 0 */
        case NS_rtf::LN_rgbrc:
        {
            writerfilter::Reference<Properties>::Pointer_t pProperties = rVal.getProperties();
            if( pProperties.get())
            {
                pProperties->resolve(*this);
                ConversionHelper::MakeBorderLine( m_nLineWidth,   m_nLineType, m_nLineColor,
                                                                                m_aBorderLines[m_nCurrentBorderPosition], m_bOOXML );
                OSL_ENSURE(m_nCurrentBorderPosition < BORDER_COUNT, "too many border values");
                ++m_nCurrentBorderPosition;
            }
        }
        break;
        /* WRITERFILTERSTATUS: done: 75, planned: 0, spent: 0 */
        case NS_rtf::LN_DPTLINEWIDTH: // 0x2871
            //  width of a single line in 1/8 pt, max of 32 pt -> twip * 5 / 2.
            m_nLineWidth = ConversionHelper::convertTwipToMM100( nIntValue * 5 / 2 );
        break;
        /* WRITERFILTERSTATUS: done: 75, planned: 0, spent: 0 */
        case NS_rtf::LN_BRCTYPE:    // 0x2872
            m_nLineType = nIntValue;
        break;
        /* WRITERFILTERSTATUS: done: 75, planned: 0, spent: 0 */
        case NS_ooxml::LN_CT_Border_color:
        case NS_rtf::LN_ICO:        // 0x2873
            m_nLineColor = nIntValue;
        break;
        /* WRITERFILTERSTATUS: done: 100, planned: 0, spent: 0 */
        case NS_rtf::LN_DPTSPACE:   // border distance in points
            m_nLineDistance = ConversionHelper::convertTwipToMM100( nIntValue * 20 );
        break;
        case NS_rtf::LN_FSHADOW:    // 0x2875
            //if 1 then line has shadow - unsupported
        case NS_rtf::LN_FFRAME:     // 0x2876
        case NS_rtf::LN_UNUSED2_15: // 0x2877
            // ignored
        break;
        case NS_ooxml::LN_CT_Border_themeTint: break;
        case NS_ooxml::LN_CT_Border_themeColor: break;
        default:
            OSL_ENSURE( false, "unknown attribute");
    }
}
/*-- 24.04.2007 09:06:35---------------------------------------------------

  -----------------------------------------------------------------------*/
void BorderHandler::sprm(Sprm & rSprm)
{
#ifdef DEBUG_DOMAINMAPPER
    dmapper_logger->startElement("BorderHandler.sprm");
    dmapper_logger->attribute("sprm", rSprm.toString());
#endif

    /* WRITERFILTERSTATUS: table: BorderHandler_sprm */
    switch( rSprm.getId())
    {
        /* WRITERFILTERSTATUS: done: 75, planned: 0, spent: 0 */
        case NS_ooxml::LN_CT_TblBorders_top:
        /* WRITERFILTERSTATUS: done: 75, planned: 0, spent: 0 */
        case NS_ooxml::LN_CT_TblBorders_left:
        /* WRITERFILTERSTATUS: done: 75, planned: 0, spent: 0 */
        case NS_ooxml::LN_CT_TblBorders_bottom:
        /* WRITERFILTERSTATUS: done: 75, planned: 0, spent: 0 */
        case NS_ooxml::LN_CT_TblBorders_right:
        /* WRITERFILTERSTATUS: done: 75, planned: 0, spent: 0 */
        case NS_ooxml::LN_CT_TblBorders_insideH:
        /* WRITERFILTERSTATUS: done: 75, planned: 0, spent: 0 */
        case NS_ooxml::LN_CT_TblBorders_insideV:
        {
            writerfilter::Reference<Properties>::Pointer_t pProperties = rSprm.getProps();
            if( pProperties.get())
                pProperties->resolve(*this);
            ConversionHelper::MakeBorderLine( m_nLineWidth,   m_nLineType, m_nLineColor,
                                   m_aBorderLines[rSprm.getId() - NS_ooxml::LN_CT_TblBorders_top], m_bOOXML );

            m_aFilledLines[ rSprm.getId( ) - NS_ooxml::LN_CT_TblBorders_top] = true;
        }
        break;
        default:;
    }

#ifdef DEBUG_DOMAINMAPPER
    dmapper_logger->endElement("BorderHandler.sprm");
#endif

}
/*-- 24.04.2007 09:09:01---------------------------------------------------

  -----------------------------------------------------------------------*/
PropertyMapPtr  BorderHandler::getProperties()
{
    static const PropertyIds aPropNames[BORDER_COUNT] =
    {
        PROP_TOP_BORDER,
        PROP_LEFT_BORDER,
        PROP_BOTTOM_BORDER,
        PROP_RIGHT_BORDER,
        META_PROP_HORIZONTAL_BORDER,
        META_PROP_VERTICAL_BORDER
    };
    PropertyMapPtr pPropertyMap(new PropertyMap);
    // don't fill in default properties
    if( m_bOOXML || m_nCurrentBorderPosition )
    {
        for( sal_Int32 nProp = 0; nProp < BORDER_COUNT; ++nProp)
        {
            if ( m_aFilledLines[nProp] ) {
                pPropertyMap->Insert( aPropNames[nProp], false, uno::makeAny( m_aBorderLines[nProp] ) );
            }
        }
    }
    return pPropertyMap;
}
/*-- 14.11.2007 12:42:52---------------------------------------------------
    used only in OOXML import
  -----------------------------------------------------------------------*/
table::BorderLine2 BorderHandler::getBorderLine()
{
    table::BorderLine2 aBorderLine;
    ConversionHelper::MakeBorderLine( m_nLineWidth, m_nLineType, m_nLineColor, aBorderLine, m_bOOXML );
    return aBorderLine;
}

} //namespace dmapper
} //namespace writerfilter
<commit_msg>adjust for table::BorderLine2<commit_after>/*************************************************************************
 *
 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
 *
 * Copyright 2000, 2010 Oracle and/or its affiliates.
 *
 * OpenOffice.org - a multi-platform office productivity suite
 *
 * This file is part of OpenOffice.org.
 *
 * OpenOffice.org is free software: you can redistribute it and/or modify
 * it under the terms of the GNU Lesser General Public License version 3
 * only, as published by the Free Software Foundation.
 *
 * OpenOffice.org is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU Lesser General Public License version 3 for more details
 * (a copy is included in the LICENSE file that accompanied this code).
 *
 * You should have received a copy of the GNU Lesser General Public License
 * version 3 along with OpenOffice.org.  If not, see
 * <http://www.openoffice.org/license.html>
 * for a copy of the LGPLv3 License.
 *
 ************************************************************************/
#include <BorderHandler.hxx>
#include <PropertyMap.hxx>
#include <resourcemodel/QNameToString.hxx>
#include <doctok/resourceids.hxx>
#include <ConversionHelper.hxx>
#include <com/sun/star/table/BorderLine2.hpp>
#include <ooxml/resourceids.hxx>
#include <dmapperLoggers.hxx>

namespace writerfilter {

namespace dmapper {

using namespace ::com::sun::star;
//using namespace ::std;

/*-- 24.04.2007 09:06:35---------------------------------------------------

  -----------------------------------------------------------------------*/
BorderHandler::BorderHandler( bool bOOXML ) :
    m_nCurrentBorderPosition( BORDER_TOP ),
    m_nLineWidth(0),
    m_nLineType(0),
    m_nLineColor(0),
    m_nLineDistance(0),
    m_bOOXML( bOOXML )
{
    const int nBorderCount(BORDER_COUNT);
    std::fill_n(m_aFilledLines, nBorderCount, false);
    std::fill_n(m_aBorderLines, nBorderCount, table::BorderLine2());
}
/*-- 24.04.2007 09:06:35---------------------------------------------------

  -----------------------------------------------------------------------*/
BorderHandler::~BorderHandler()
{
}
/*-- 24.04.2007 09:06:35---------------------------------------------------

  -----------------------------------------------------------------------*/
void BorderHandler::attribute(Id rName, Value & rVal)
{
#ifdef DEBUG_DOMAINMAPPER
    dmapper_logger->startElement("BorderHandler.attribute");
    dmapper_logger->attribute("id", (*QNameToString::Instance())(rName));
    dmapper_logger->endElement("BorderHandler.attribute");
#endif

    sal_Int32 nIntValue = rVal.getInt();
    /* WRITERFILTERSTATUS: table: BorderHandler_attributedata */
    switch( rName )
    {
        /* WRITERFILTERSTATUS: done: 75, planned: 0, spent: 0 */
        case NS_rtf::LN_rgbrc:
        {
            writerfilter::Reference<Properties>::Pointer_t pProperties = rVal.getProperties();
            if( pProperties.get())
            {
                pProperties->resolve(*this);
                ConversionHelper::MakeBorderLine( m_nLineWidth,   m_nLineType, m_nLineColor,
                                                                                m_aBorderLines[m_nCurrentBorderPosition], m_bOOXML );
                OSL_ENSURE(m_nCurrentBorderPosition < BORDER_COUNT, "too many border values");
                ++m_nCurrentBorderPosition;
            }
        }
        break;
        /* WRITERFILTERSTATUS: done: 75, planned: 0, spent: 0 */
        case NS_rtf::LN_DPTLINEWIDTH: // 0x2871
            //  width of a single line in 1/8 pt, max of 32 pt -> twip * 5 / 2.
            m_nLineWidth = ConversionHelper::convertTwipToMM100( nIntValue * 5 / 2 );
        break;
        /* WRITERFILTERSTATUS: done: 75, planned: 0, spent: 0 */
        case NS_rtf::LN_BRCTYPE:    // 0x2872
            m_nLineType = nIntValue;
        break;
        /* WRITERFILTERSTATUS: done: 75, planned: 0, spent: 0 */
        case NS_ooxml::LN_CT_Border_color:
        case NS_rtf::LN_ICO:        // 0x2873
            m_nLineColor = nIntValue;
        break;
        /* WRITERFILTERSTATUS: done: 100, planned: 0, spent: 0 */
        case NS_rtf::LN_DPTSPACE:   // border distance in points
            m_nLineDistance = ConversionHelper::convertTwipToMM100( nIntValue * 20 );
        break;
        case NS_rtf::LN_FSHADOW:    // 0x2875
            //if 1 then line has shadow - unsupported
        case NS_rtf::LN_FFRAME:     // 0x2876
        case NS_rtf::LN_UNUSED2_15: // 0x2877
            // ignored
        break;
        case NS_ooxml::LN_CT_Border_themeTint: break;
        case NS_ooxml::LN_CT_Border_themeColor: break;
        default:
            OSL_ENSURE( false, "unknown attribute");
    }
}
/*-- 24.04.2007 09:06:35---------------------------------------------------

  -----------------------------------------------------------------------*/
void BorderHandler::sprm(Sprm & rSprm)
{
#ifdef DEBUG_DOMAINMAPPER
    dmapper_logger->startElement("BorderHandler.sprm");
    dmapper_logger->attribute("sprm", rSprm.toString());
#endif

    /* WRITERFILTERSTATUS: table: BorderHandler_sprm */
    switch( rSprm.getId())
    {
        /* WRITERFILTERSTATUS: done: 75, planned: 0, spent: 0 */
        case NS_ooxml::LN_CT_TblBorders_top:
        /* WRITERFILTERSTATUS: done: 75, planned: 0, spent: 0 */
        case NS_ooxml::LN_CT_TblBorders_left:
        /* WRITERFILTERSTATUS: done: 75, planned: 0, spent: 0 */
        case NS_ooxml::LN_CT_TblBorders_bottom:
        /* WRITERFILTERSTATUS: done: 75, planned: 0, spent: 0 */
        case NS_ooxml::LN_CT_TblBorders_right:
        /* WRITERFILTERSTATUS: done: 75, planned: 0, spent: 0 */
        case NS_ooxml::LN_CT_TblBorders_insideH:
        /* WRITERFILTERSTATUS: done: 75, planned: 0, spent: 0 */
        case NS_ooxml::LN_CT_TblBorders_insideV:
        {
            writerfilter::Reference<Properties>::Pointer_t pProperties = rSprm.getProps();
            if( pProperties.get())
                pProperties->resolve(*this);
            ConversionHelper::MakeBorderLine( m_nLineWidth,   m_nLineType, m_nLineColor,
                                   m_aBorderLines[rSprm.getId() - NS_ooxml::LN_CT_TblBorders_top], m_bOOXML );

            m_aFilledLines[ rSprm.getId( ) - NS_ooxml::LN_CT_TblBorders_top] = true;
        }
        break;
        default:;
    }

#ifdef DEBUG_DOMAINMAPPER
    dmapper_logger->endElement("BorderHandler.sprm");
#endif

}
/*-- 24.04.2007 09:09:01---------------------------------------------------

  -----------------------------------------------------------------------*/
PropertyMapPtr  BorderHandler::getProperties()
{
    static const PropertyIds aPropNames[BORDER_COUNT] =
    {
        PROP_TOP_BORDER,
        PROP_LEFT_BORDER,
        PROP_BOTTOM_BORDER,
        PROP_RIGHT_BORDER,
        META_PROP_HORIZONTAL_BORDER,
        META_PROP_VERTICAL_BORDER
    };
    PropertyMapPtr pPropertyMap(new PropertyMap);
    // don't fill in default properties
    if( m_bOOXML || m_nCurrentBorderPosition )
    {
        for( sal_Int32 nProp = 0; nProp < BORDER_COUNT; ++nProp)
        {
            if ( m_aFilledLines[nProp] ) {
                pPropertyMap->Insert( aPropNames[nProp], false, uno::makeAny( m_aBorderLines[nProp] ) );
            }
        }
    }
    return pPropertyMap;
}
/*-- 14.11.2007 12:42:52---------------------------------------------------
    used only in OOXML import
  -----------------------------------------------------------------------*/
table::BorderLine2 BorderHandler::getBorderLine()
{
    table::BorderLine2 aBorderLine;
    ConversionHelper::MakeBorderLine( m_nLineWidth, m_nLineType, m_nLineColor, aBorderLine, m_bOOXML );
    return aBorderLine;
}

} //namespace dmapper
} //namespace writerfilter
<|endoftext|>


<commit_before>#include "MajorityElement.hpp"

#include <iostream>

int MajorityElement::majorityElement(vector<int>& nums)
{
    int result = nums[0];
    int counter = 1;
    for (int i = 1; i < nums.size(); i++) {
        counter += (nums[i] == result) ? 1 : -1;
        if (counter == 0) {
            result = nums[i];
            counter = 1;
        }
    }
    return result;
}<commit_msg>Code cleanup.<commit_after>#include "MajorityElement.hpp"

int MajorityElement::majorityElement(vector<int>& nums)
{
    int result = nums[0];
    int counter = 1;
    for (int i = 1; i < nums.size(); i++) {
        counter += (nums[i] == result) ? 1 : -1;
        if (counter == 0) {
            result = nums[i];
            counter = 1;
        }
    }
    return result;
}<|endoftext|>


<commit_before>/*=========================================================================

  Program:   Visualization Library
  Module:    Scalars.hh
  Language:  C++
  Date:      $Date$
  Version:   $Revision$

This file is part of the Visualization Library. No part of this file or its
contents may be copied, reproduced or altered in any way without the express
written consent of the authors.

Copyright (c) Ken Martin, Will Schroeder, Bill Lorensen 1993, 1994

=========================================================================*/
// .NAME vlScalars - abstract interface to array of scalar data
// .SECTION Description
// vlScalars provides an abstract interface to an array of scalar data.
// The data model for vlScalars is an array accessible by point id.
// The subclasses of vlScalars are concrete data types (float, int, etc.)
// that implement the interface of vlScalars.
//    Scalars typically provide a single value per point. However, there are
// types of scalars that have multiple values per point (e.g., vlPixmap or
// vlAPixmap that provide three and four values per point, respectively).
// These are used when reading data in rgb and rgba form (e.g., images
// and volumes).
//    Because of the close relationship between scalars and colors, scalars
// also maintain in internal lookup table. If provided, this table is used
// to map scalars into colors, rather than the lookup table that the vlMapper
// objects are associated with.

#ifndef __vlScalars_h
#define __vlScalars_h

#include "RefCount.hh"

class vlIdList;
class vlFloatScalars;
class vlLookupTable;

class vlScalars : public vlRefCount
{
public:
  vlScalars();
  virtual ~vlScalars();
  char *GetClassName() {return "vlScalars";};
  void PrintSelf(ostream& os, vlIndent indent);

  // Description:
  // Create a copy of this object.
  virtual vlScalars *MakeObject(int sze, int ext=1000) = 0;

  // Description:
  // Return number of points in array.
  virtual int GetNumberOfScalars() = 0;

  // Description:
  // Return the number of values per point. Should range between (1,4).
  // Used to distinguish between color scalars and single-valued ones.
  virtual int GetNumberOfValuesPerPoint() {return 1;};

  // Description:
  // Return a float scalar value for a particular point id.
  virtual float GetScalar(int id) = 0;

  // Description:
  // Insert scalar into array. No range checking performed (fast!).
  virtual void SetScalar(int id, float s) = 0;

  // Description:
  // Insert scalar into array. Range checking performed and memory
  // allocated as necessary.
  virtual void InsertScalar(int id, float s) = 0;

  // Description:
  // Insert scalar into next available slot. Returns point id of slot.
  virtual int InsertNextScalar(float s) = 0;

  // Description:
  // Reclaim any extra memory.
  virtual void Squeeze() = 0;

  // Description:
  // Get data as pointer to unsigned char. Used for high performance texture
  // and color manipulation. Not supported for all scalar types: check for
  // NULL return value.
  virtual unsigned char *GetUCharPtr() {return NULL;};

  void GetScalars(vlIdList& ptId, vlFloatScalars& fs);
  virtual void ComputeRange();
  float *GetRange();
  void GetRange(float range[8]);

  // Description:
  // Create default lookup table. Generally used to create one when none
  // is available.
  virtual void CreateDefaultLookupTable();

  void SetLookupTable(vlLookupTable *lut);
  void SetLookupTable(vlLookupTable& lut) {this->SetLookupTable(&lut);};
  vlGetObjectMacro(LookupTable,vlLookupTable);

protected:
  float Range[8];
  vlTimeStamp ComputeTime; // Time at which range computed
  vlLookupTable *LookupTable;
};

// These include files are placed here so that if Scalars.hh is included
// all other classes necessary for compilation are also included.
#include "IdList.hh"
#include "FScalars.hh"
#include "Lut.hh"

#endif
<commit_msg>remove a func that was cusing problems<commit_after>/*=========================================================================

  Program:   Visualization Library
  Module:    Scalars.hh
  Language:  C++
  Date:      $Date$
  Version:   $Revision$

This file is part of the Visualization Library. No part of this file or its
contents may be copied, reproduced or altered in any way without the express
written consent of the authors.

Copyright (c) Ken Martin, Will Schroeder, Bill Lorensen 1993, 1994

=========================================================================*/
// .NAME vlScalars - abstract interface to array of scalar data
// .SECTION Description
// vlScalars provides an abstract interface to an array of scalar data.
// The data model for vlScalars is an array accessible by point id.
// The subclasses of vlScalars are concrete data types (float, int, etc.)
// that implement the interface of vlScalars.
//    Scalars typically provide a single value per point. However, there are
// types of scalars that have multiple values per point (e.g., vlPixmap or
// vlAPixmap that provide three and four values per point, respectively).
// These are used when reading data in rgb and rgba form (e.g., images
// and volumes).
//    Because of the close relationship between scalars and colors, scalars
// also maintain in internal lookup table. If provided, this table is used
// to map scalars into colors, rather than the lookup table that the vlMapper
// objects are associated with.

#ifndef __vlScalars_h
#define __vlScalars_h

#include "RefCount.hh"

class vlIdList;
class vlFloatScalars;
class vlLookupTable;

class vlScalars : public vlRefCount
{
public:
  vlScalars();
  virtual ~vlScalars();
  char *GetClassName() {return "vlScalars";};
  void PrintSelf(ostream& os, vlIndent indent);

  // Description:
  // Create a copy of this object.
  virtual vlScalars *MakeObject(int sze, int ext=1000) = 0;

  // Description:
  // Return number of points in array.
  virtual int GetNumberOfScalars() = 0;

  // Description:
  // Return the number of values per point. Should range between (1,4).
  // Used to distinguish between color scalars and single-valued ones.
  virtual int GetNumberOfValuesPerPoint() {return 1;};

  // Description:
  // Return a float scalar value for a particular point id.
  virtual float GetScalar(int id) = 0;

  // Description:
  // Insert scalar into array. No range checking performed (fast!).
  virtual void SetScalar(int id, float s) = 0;

  // Description:
  // Insert scalar into array. Range checking performed and memory
  // allocated as necessary.
  virtual void InsertScalar(int id, float s) = 0;

  // Description:
  // Insert scalar into next available slot. Returns point id of slot.
  virtual int InsertNextScalar(float s) = 0;

  // Description:
  // Reclaim any extra memory.
  virtual void Squeeze() = 0;

  // Description:
  // Get data as pointer to unsigned char. Used for high performance texture
  // and color manipulation. Not supported for all scalar types: check for
  // NULL return value.
  virtual unsigned char *GetUCharPtr() {return NULL;};

  void GetScalars(vlIdList& ptId, vlFloatScalars& fs);
  virtual void ComputeRange();
  float *GetRange();
  void GetRange(float range[8]);

  // Description:
  // Create default lookup table. Generally used to create one when none
  // is available.
  virtual void CreateDefaultLookupTable();

  void SetLookupTable(vlLookupTable *lut);
  vlGetObjectMacro(LookupTable,vlLookupTable);

protected:
  float Range[8];
  vlTimeStamp ComputeTime; // Time at which range computed
  vlLookupTable *LookupTable;
};

// These include files are placed here so that if Scalars.hh is included
// all other classes necessary for compilation are also included.
#include "IdList.hh"
#include "FScalars.hh"

#endif
<|endoftext|>


<commit_before>#ifndef HECLFRONTEND_HPP
#define HECLFRONTEND_HPP

#include <string>
#include <vector>
#include <forward_list>
#include <athena/Types.hpp>
#include <athena/DNA.hpp>
#include <hecl/hecl.hpp>

namespace hecl
{
namespace Frontend
{

struct SourceLocation
{
    int line = -1;
    int col = -1;
    SourceLocation() = default;
    SourceLocation(int l, int c) : line(l), col(c) {}
};

class Diagnostics
{
    std::string m_name;
    std::string m_source;
    std::string m_backend = "Backend";
    std::string sourceDiagString(const SourceLocation& l, bool ansi=false) const;
public:
    void reset(const std::string& name, const std::string& source) {m_name = name; m_source = source;}
    void reset(const std::string& name) {m_name = name; m_source.clear();}
    void setBackend(const std::string& backend) {m_backend = backend;}
    void setBackend(const char* backend) {m_backend = backend;}
    void reportParserErr(const SourceLocation& l, const char* format, ...);
    void reportLexerErr(const SourceLocation& l, const char* format, ...);
    void reportCompileErr(const SourceLocation& l, const char* format, ...);
    void reportBackendErr(const SourceLocation& l, const char* format, ...);

    const std::string& getName() const {return m_name;}
    const std::string& getSource() const {return m_source;}
};

class Parser
{
public:
    enum class TokenType
    {
        None,
        SourceBegin,
        SourceEnd,
        NumLiteral,
        VectorSwizzle,
        EvalGroupStart,
        EvalGroupEnd,
        FunctionStart,
        FunctionEnd,
        FunctionArgDelim,
        ArithmeticOp,
    };
private:
    Diagnostics& m_diag;
    const std::string* m_source = nullptr;
    std::string::const_iterator m_sourceIt;
    std::vector<TokenType> m_parenStack;
    bool m_reset = false;
    void skipWhitespace(std::string::const_iterator& it);
public:
    struct Token
    {
        TokenType m_type;
        SourceLocation m_location;
        std::string m_tokenString;
        int m_tokenInt = 0;
        float m_tokenFloat = 0.0;
        Token() : m_type(TokenType::None) {}
        Token(TokenType type, SourceLocation loc) : m_type(type), m_location(loc) {}
        const char* typeString() const
        {
            switch (m_type)
            {
            case TokenType::None:
                return "None";
            case TokenType::SourceBegin:
                return "SourceBegin";
            case TokenType::SourceEnd:
                return "SourceEnd";
            case TokenType::NumLiteral:
                return "NumLiteral";
            case TokenType::VectorSwizzle:
                return "VectorSwizzle";
            case TokenType::EvalGroupStart:
                return "EvalGroupStart";
            case TokenType::EvalGroupEnd:
                return "EvalGroupEnd";
            case TokenType::FunctionStart:
                return "FunctionStart";
            case TokenType::FunctionEnd:
                return "FunctionEnd";
            case TokenType::FunctionArgDelim:
                return "FunctionArgDelim";
            case TokenType::ArithmeticOp:
                return "ArithmeticOp";
            default: break;
            }
            return nullptr;
        }
    };
    void reset(const std::string& source);
    Token consumeToken();
    SourceLocation getLocation() const;

    Parser(Diagnostics& diag) : m_diag(diag) {}
};

using BigDNA = athena::io::DNA<athena::BigEndian>;

struct IR : BigDNA
{
    Delete _d;

    enum OpType : uint8_t
    {
        None,       /**< NOP */
        Call,       /**< Deferred function insertion for HECL backend using specified I/O regs */
        LoadImm,    /**< Load a constant (numeric literal) into register */
        Arithmetic, /**< Perform binary arithmetic between registers */
        Swizzle     /**< Vector insertion/extraction/swizzling operation */
    };

    using RegID = atUint16;

    struct Instruction : BigDNA
    {
        Delete _d;

        OpType m_op = OpType::None;
        RegID m_target = RegID(-1);
        SourceLocation m_loc;

        struct Call : BigDNA
        {
            DECL_DNA
            String<-1> m_name;
            Value<atUint16> m_argInstCount;
            Vector<atUint16, DNA_COUNT(m_argInstCount)> m_argInstIdxs;
        } m_call;

        struct LoadImm : BigDNA
        {
            DECL_DNA
            Value<atVec4f> m_immVec = {};
        } m_loadImm;

        enum ArithmeticOpType : uint8_t
        {
            None,
            Add,
            Subtract,
            Multiply,
            Divide
        };

        struct Arithmetic : BigDNA
        {
            DECL_DNA
            Value<ArithmeticOpType> m_op = ArithmeticOpType::None;
            Value<atUint16> m_instIdxs[2];
        } m_arithmetic;

        struct Swizzle : BigDNA
        {
            DECL_DNA
            Value<atInt8> m_idxs[4] = {-1, -1, -1, -1};
            Value<atUint16> m_instIdx;
        } m_swizzle;

        Instruction(OpType type, const SourceLocation& loc) : m_op(type), m_loc(loc) {}

        int getChildCount() const
        {
            switch (m_op)
            {
            case OpType::Call:
                return m_call.m_argInstIdxs.size();
            case OpType::Arithmetic:
                return 2;
            case OpType::Swizzle:
                return 1;
            default:
                LogModule.report(logvisor::Fatal, "invalid op type");
            }
            return -1;
        }

        const IR::Instruction& getChildInst(const IR& ir, size_t idx) const
        {
            switch (m_op)
            {
            case OpType::Call:
                return ir.m_instructions.at(m_call.m_argInstIdxs.at(idx));
            case OpType::Arithmetic:
                if (idx > 1)
                    LogModule.report(logvisor::Fatal, "arithmetic child idx must be 0 or 1");
                return ir.m_instructions.at(m_arithmetic.m_instIdxs[idx]);
            case OpType::Swizzle:
                if (idx > 0)
                    LogModule.report(logvisor::Fatal, "swizzle child idx must be 0");
                return ir.m_instructions.at(m_swizzle.m_instIdx);
            default:
                LogModule.report(logvisor::Fatal, "invalid op type");
            }
            return *this;
        }

        const atVec4f& getImmVec() const
        {
            if (m_op != OpType::LoadImm)
                LogModule.report(logvisor::Fatal, "invalid op type");
            return m_loadImm.m_immVec;
        }

        void read(athena::io::IStreamReader& reader)
        {
            m_op = OpType(reader.readUByte());
            m_target = reader.readUint16Big();
            switch (m_op)
            {
            default: break;
            case OpType::Call:
                m_call.read(reader);
                break;
            case OpType::LoadImm:
                m_loadImm.read(reader);
                break;
            case OpType::Arithmetic:
                m_arithmetic.read(reader);
                break;
            case OpType::Swizzle:
                m_swizzle.read(reader);
                break;
            }
        }

        void write(athena::io::IStreamWriter& writer) const
        {
            writer.writeUByte(m_op);
            writer.writeUint16Big(m_target);
            switch (m_op)
            {
            default: break;
            case OpType::Call:
                m_call.write(writer);
                break;
            case OpType::LoadImm:
                m_loadImm.write(writer);
                break;
            case OpType::Arithmetic:
                m_arithmetic.write(writer);
                break;
            case OpType::Swizzle:
                m_swizzle.write(writer);
                break;
            }
        }

        size_t binarySize(size_t sz) const
        {
            sz += 3;
            switch (m_op)
            {
            default: break;
            case OpType::Call:
                sz = m_call.binarySize(sz);
                break;
            case OpType::LoadImm:
                sz = m_loadImm.binarySize(sz);
                break;
            case OpType::Arithmetic:
                sz = m_arithmetic.binarySize(sz);
                break;
            case OpType::Swizzle:
                sz = m_swizzle.binarySize(sz);
                break;
            }
            return sz;
        }

        Instruction(athena::io::IStreamReader& reader) {read(reader);}
    };

    atUint64 m_hash = 0;
    atUint16 m_regCount = 0;
    std::vector<Instruction> m_instructions;

    void read(athena::io::IStreamReader& reader)
    {
        m_hash = reader.readUint64Big();
        m_regCount = reader.readUint16Big();
        atUint16 instCount = reader.readUint16Big();
        m_instructions.clear();
        m_instructions.reserve(instCount);
        for (atUint16 i=0 ; i<instCount ; ++i)
            m_instructions.emplace_back(reader);
    }

    void write(athena::io::IStreamWriter& writer) const
    {
        writer.writeUint64Big(m_hash);
        writer.writeUint16Big(m_regCount);
        writer.writeUint16Big(m_instructions.size());
        for (const Instruction& inst : m_instructions)
            inst.write(writer);
    }

    size_t binarySize(size_t sz) const
    {
        sz += 12;
        for (const Instruction& inst : m_instructions)
            sz = inst.binarySize(sz);
        return sz;
    }
};

class Lexer
{
    friend class OperationNode;
    Diagnostics& m_diag;

    /* Intermediate tree-node for organizing tokens into operations */
    struct OperationNode
    {
        Parser::Token m_tok;
        OperationNode* m_prev = nullptr;
        OperationNode* m_next = nullptr;
        OperationNode* m_sub = nullptr;

        OperationNode() {}
        OperationNode(Parser::Token&& tok) : m_tok(std::move(tok)) {}
    };

    /* Pool of nodes to keep ownership (forward_list so pointers aren't invalidated) */
    std::forward_list<OperationNode> m_pool;

    /* Final lexed root function (IR comes from this) */
    OperationNode* m_root = nullptr;

    /* Helper for relinking operator precedence */
    void ReconnectArithmetic(OperationNode* sn, OperationNode** lastSub, OperationNode** newSub) const;

    /* Recursive IR compile funcs */
    void RecursiveFuncCompile(IR& ir, const Lexer::OperationNode* funcNode, IR::RegID target) const;
    void RecursiveGroupCompile(IR& ir, const Lexer::OperationNode* groupNode, IR::RegID target) const;
    void EmitVec3(IR& ir, const Lexer::OperationNode* funcNode, IR::RegID target) const;
    void EmitVec4(IR& ir, const Lexer::OperationNode* funcNode, IR::RegID target) const;
    void EmitArithmetic(IR& ir, const Lexer::OperationNode* arithNode, IR::RegID target) const;
    void EmitVectorSwizzle(IR& ir, const Lexer::OperationNode* swizNode, IR::RegID target) const;

    static void PrintChain(const Lexer::OperationNode* begin, const Lexer::OperationNode* end);
    static void PrintTree(const Lexer::OperationNode* node, int indent=0);

public:
    void reset();
    void consumeAllTokens(Parser& parser);
    IR compileIR(atUint64 hash) const;

    Lexer(Diagnostics& diag) : m_diag(diag) {}
};

class Frontend
{
    Diagnostics m_diag;
    Parser m_parser;
    Lexer m_lexer;
public:
    IR compileSource(const std::string& source, const std::string& diagName)
    {
        Hash hash(source);
        m_diag.reset(diagName, source);
        m_parser.reset(source);
        m_lexer.consumeAllTokens(m_parser);
        return m_lexer.compileIR(hash);
    }

    Diagnostics& getDiagnostics() {return m_diag;}

    Frontend() : m_parser(m_diag), m_lexer(m_diag) {}
};

}
}

#endif // HECLFRONTEND_HPP
<commit_msg>Shader hash fix<commit_after>#ifndef HECLFRONTEND_HPP
#define HECLFRONTEND_HPP

#include <string>
#include <vector>
#include <forward_list>
#include <athena/Types.hpp>
#include <athena/DNA.hpp>
#include <hecl/hecl.hpp>

namespace hecl
{
namespace Frontend
{

struct SourceLocation
{
    int line = -1;
    int col = -1;
    SourceLocation() = default;
    SourceLocation(int l, int c) : line(l), col(c) {}
};

class Diagnostics
{
    std::string m_name;
    std::string m_source;
    std::string m_backend = "Backend";
    std::string sourceDiagString(const SourceLocation& l, bool ansi=false) const;
public:
    void reset(const std::string& name, const std::string& source) {m_name = name; m_source = source;}
    void reset(const std::string& name) {m_name = name; m_source.clear();}
    void setBackend(const std::string& backend) {m_backend = backend;}
    void setBackend(const char* backend) {m_backend = backend;}
    void reportParserErr(const SourceLocation& l, const char* format, ...);
    void reportLexerErr(const SourceLocation& l, const char* format, ...);
    void reportCompileErr(const SourceLocation& l, const char* format, ...);
    void reportBackendErr(const SourceLocation& l, const char* format, ...);

    const std::string& getName() const {return m_name;}
    const std::string& getSource() const {return m_source;}
};

class Parser
{
public:
    enum class TokenType
    {
        None,
        SourceBegin,
        SourceEnd,
        NumLiteral,
        VectorSwizzle,
        EvalGroupStart,
        EvalGroupEnd,
        FunctionStart,
        FunctionEnd,
        FunctionArgDelim,
        ArithmeticOp,
    };
private:
    Diagnostics& m_diag;
    const std::string* m_source = nullptr;
    std::string::const_iterator m_sourceIt;
    std::vector<TokenType> m_parenStack;
    bool m_reset = false;
    void skipWhitespace(std::string::const_iterator& it);
public:
    struct Token
    {
        TokenType m_type;
        SourceLocation m_location;
        std::string m_tokenString;
        int m_tokenInt = 0;
        float m_tokenFloat = 0.0;
        Token() : m_type(TokenType::None) {}
        Token(TokenType type, SourceLocation loc) : m_type(type), m_location(loc) {}
        const char* typeString() const
        {
            switch (m_type)
            {
            case TokenType::None:
                return "None";
            case TokenType::SourceBegin:
                return "SourceBegin";
            case TokenType::SourceEnd:
                return "SourceEnd";
            case TokenType::NumLiteral:
                return "NumLiteral";
            case TokenType::VectorSwizzle:
                return "VectorSwizzle";
            case TokenType::EvalGroupStart:
                return "EvalGroupStart";
            case TokenType::EvalGroupEnd:
                return "EvalGroupEnd";
            case TokenType::FunctionStart:
                return "FunctionStart";
            case TokenType::FunctionEnd:
                return "FunctionEnd";
            case TokenType::FunctionArgDelim:
                return "FunctionArgDelim";
            case TokenType::ArithmeticOp:
                return "ArithmeticOp";
            default: break;
            }
            return nullptr;
        }
    };
    void reset(const std::string& source);
    Token consumeToken();
    SourceLocation getLocation() const;

    Parser(Diagnostics& diag) : m_diag(diag) {}
};

using BigDNA = athena::io::DNA<athena::BigEndian>;

struct IR : BigDNA
{
    Delete _d;

    enum OpType : uint8_t
    {
        None,       /**< NOP */
        Call,       /**< Deferred function insertion for HECL backend using specified I/O regs */
        LoadImm,    /**< Load a constant (numeric literal) into register */
        Arithmetic, /**< Perform binary arithmetic between registers */
        Swizzle     /**< Vector insertion/extraction/swizzling operation */
    };

    using RegID = atUint16;

    struct Instruction : BigDNA
    {
        Delete _d;

        OpType m_op = OpType::None;
        RegID m_target = RegID(-1);
        SourceLocation m_loc;

        struct Call : BigDNA
        {
            DECL_DNA
            String<-1> m_name;
            Value<atUint16> m_argInstCount;
            Vector<atUint16, DNA_COUNT(m_argInstCount)> m_argInstIdxs;
        } m_call;

        struct LoadImm : BigDNA
        {
            DECL_DNA
            Value<atVec4f> m_immVec = {};
        } m_loadImm;

        enum ArithmeticOpType : uint8_t
        {
            None,
            Add,
            Subtract,
            Multiply,
            Divide
        };

        struct Arithmetic : BigDNA
        {
            DECL_DNA
            Value<ArithmeticOpType> m_op = ArithmeticOpType::None;
            Value<atUint16> m_instIdxs[2];
        } m_arithmetic;

        struct Swizzle : BigDNA
        {
            DECL_DNA
            Value<atInt8> m_idxs[4] = {-1, -1, -1, -1};
            Value<atUint16> m_instIdx;
        } m_swizzle;

        Instruction(OpType type, const SourceLocation& loc) : m_op(type), m_loc(loc) {}

        int getChildCount() const
        {
            switch (m_op)
            {
            case OpType::Call:
                return m_call.m_argInstIdxs.size();
            case OpType::Arithmetic:
                return 2;
            case OpType::Swizzle:
                return 1;
            default:
                LogModule.report(logvisor::Fatal, "invalid op type");
            }
            return -1;
        }

        const IR::Instruction& getChildInst(const IR& ir, size_t idx) const
        {
            switch (m_op)
            {
            case OpType::Call:
                return ir.m_instructions.at(m_call.m_argInstIdxs.at(idx));
            case OpType::Arithmetic:
                if (idx > 1)
                    LogModule.report(logvisor::Fatal, "arithmetic child idx must be 0 or 1");
                return ir.m_instructions.at(m_arithmetic.m_instIdxs[idx]);
            case OpType::Swizzle:
                if (idx > 0)
                    LogModule.report(logvisor::Fatal, "swizzle child idx must be 0");
                return ir.m_instructions.at(m_swizzle.m_instIdx);
            default:
                LogModule.report(logvisor::Fatal, "invalid op type");
            }
            return *this;
        }

        const atVec4f& getImmVec() const
        {
            if (m_op != OpType::LoadImm)
                LogModule.report(logvisor::Fatal, "invalid op type");
            return m_loadImm.m_immVec;
        }

        void read(athena::io::IStreamReader& reader)
        {
            m_op = OpType(reader.readUByte());
            m_target = reader.readUint16Big();
            switch (m_op)
            {
            default: break;
            case OpType::Call:
                m_call.read(reader);
                break;
            case OpType::LoadImm:
                m_loadImm.read(reader);
                break;
            case OpType::Arithmetic:
                m_arithmetic.read(reader);
                break;
            case OpType::Swizzle:
                m_swizzle.read(reader);
                break;
            }
        }

        void write(athena::io::IStreamWriter& writer) const
        {
            writer.writeUByte(m_op);
            writer.writeUint16Big(m_target);
            switch (m_op)
            {
            default: break;
            case OpType::Call:
                m_call.write(writer);
                break;
            case OpType::LoadImm:
                m_loadImm.write(writer);
                break;
            case OpType::Arithmetic:
                m_arithmetic.write(writer);
                break;
            case OpType::Swizzle:
                m_swizzle.write(writer);
                break;
            }
        }

        size_t binarySize(size_t sz) const
        {
            sz += 3;
            switch (m_op)
            {
            default: break;
            case OpType::Call:
                sz = m_call.binarySize(sz);
                break;
            case OpType::LoadImm:
                sz = m_loadImm.binarySize(sz);
                break;
            case OpType::Arithmetic:
                sz = m_arithmetic.binarySize(sz);
                break;
            case OpType::Swizzle:
                sz = m_swizzle.binarySize(sz);
                break;
            }
            return sz;
        }

        Instruction(athena::io::IStreamReader& reader) {read(reader);}
    };

    atUint64 m_hash = 0;
    atUint16 m_regCount = 0;
    std::vector<Instruction> m_instructions;

    void read(athena::io::IStreamReader& reader)
    {
        m_hash = reader.readUint64Big();
        m_regCount = reader.readUint16Big();
        atUint16 instCount = reader.readUint16Big();
        m_instructions.clear();
        m_instructions.reserve(instCount);
        for (atUint16 i=0 ; i<instCount ; ++i)
            m_instructions.emplace_back(reader);
    }

    void write(athena::io::IStreamWriter& writer) const
    {
        writer.writeUint64Big(m_hash);
        writer.writeUint16Big(m_regCount);
        writer.writeUint16Big(m_instructions.size());
        for (const Instruction& inst : m_instructions)
            inst.write(writer);
    }

    size_t binarySize(size_t sz) const
    {
        sz += 12;
        for (const Instruction& inst : m_instructions)
            sz = inst.binarySize(sz);
        return sz;
    }
};

class Lexer
{
    friend class OperationNode;
    Diagnostics& m_diag;

    /* Intermediate tree-node for organizing tokens into operations */
    struct OperationNode
    {
        Parser::Token m_tok;
        OperationNode* m_prev = nullptr;
        OperationNode* m_next = nullptr;
        OperationNode* m_sub = nullptr;

        OperationNode() {}
        OperationNode(Parser::Token&& tok) : m_tok(std::move(tok)) {}
    };

    /* Pool of nodes to keep ownership (forward_list so pointers aren't invalidated) */
    std::forward_list<OperationNode> m_pool;

    /* Final lexed root function (IR comes from this) */
    OperationNode* m_root = nullptr;

    /* Helper for relinking operator precedence */
    void ReconnectArithmetic(OperationNode* sn, OperationNode** lastSub, OperationNode** newSub) const;

    /* Recursive IR compile funcs */
    void RecursiveFuncCompile(IR& ir, const Lexer::OperationNode* funcNode, IR::RegID target) const;
    void RecursiveGroupCompile(IR& ir, const Lexer::OperationNode* groupNode, IR::RegID target) const;
    void EmitVec3(IR& ir, const Lexer::OperationNode* funcNode, IR::RegID target) const;
    void EmitVec4(IR& ir, const Lexer::OperationNode* funcNode, IR::RegID target) const;
    void EmitArithmetic(IR& ir, const Lexer::OperationNode* arithNode, IR::RegID target) const;
    void EmitVectorSwizzle(IR& ir, const Lexer::OperationNode* swizNode, IR::RegID target) const;

    static void PrintChain(const Lexer::OperationNode* begin, const Lexer::OperationNode* end);
    static void PrintTree(const Lexer::OperationNode* node, int indent=0);

public:
    void reset();
    void consumeAllTokens(Parser& parser);
    IR compileIR(atUint64 hash) const;

    Lexer(Diagnostics& diag) : m_diag(diag) {}
};

class Frontend
{
    Diagnostics m_diag;
    Parser m_parser;
    Lexer m_lexer;
public:
    IR compileSource(const std::string& source, const std::string& diagName)
    {
        Hash hash(source);
        m_diag.reset(diagName, source);
        m_parser.reset(source);
        m_lexer.consumeAllTokens(m_parser);
        return m_lexer.compileIR(hash.val64());
    }

    Diagnostics& getDiagnostics() {return m_diag;}

    Frontend() : m_parser(m_diag), m_lexer(m_diag) {}
};

}
}

#endif // HECLFRONTEND_HPP
<|endoftext|>