<commit_before>/*
 * TTBlue Limiter Object
 * Copyright © 2008, Timothy Place
 *
 * License: This code is licensed under the terms of the GNU LGPL
 * http://www.gnu.org/licenses/lgpl.html
 */

#include "TTLimiter.h"
#define thisTTClass TTLimiter

TTLimiter::TTLimiter(TTUInt16 newMaxNumChannels)
	: TTAudioObject("audio.limiter", newMaxNumChannels),
	lookaheadBuffer(NULL), lookaheadBufferIndex(0), gain(NULL), last(0.0), recover(0.0), maxBufferSize(512), attrMode(TT("exponential"))
{
	// register our attributes
	registerAttribute(TT("preamp"),		kTypeFloat64,	NULL,			(TTGetterMethod)&TTLimiter::getPreamp,		(TTSetterMethod)&TTLimiter::setPreamp);
	registerAttribute(TT("postamp"),	kTypeFloat64,	&attrPostamp,	(TTGetterMethod)&TTLimiter::getPostamp,		(TTSetterMethod)&TTLimiter::setPostamp);
	registerAttribute(TT("threshold"),	kTypeFloat64,	&attrThreshold,	(TTGetterMethod)&TTLimiter::getThreshold,	(TTSetterMethod)&TTLimiter::setThreshold);
	registerAttribute(TT("lookahead"),	kTypeUInt32,	&attrLookahead, (TTSetterMethod)&TTLimiter::setLookahead);
	registerAttribute(TT("release"),	kTypeFloat64,	&attrRelease,	(TTSetterMethod)&TTLimiter::setRelease);
	registerAttribute(TT("mode"),		kTypeSymbol,	&attrMode,		(TTSetterMethod)&TTLimiter::setMode);
	registerAttribute(TT("dcBlocker"),	kTypeBoolean,	&attrDCBlocker,	(TTSetterMethod)&TTLimiter::setDCBlocker);

	// register for notifications from the parent class so we can allocate memory as required
	registerMessageWithArgument(updateMaxNumChannels);
	// register for notifications from the parent class so we can update the release/recover values
	registerMessageSimple(updateSr);
	// clear the history
	registerMessageSimple(clear);

	dcBlocker = new TTDCBlock(maxNumChannels);
	preamp = new TTGain(maxNumChannels);

	// Set Defaults...
	setAttributeValue(TT("maxNumChannels"),	newMaxNumChannels);
	setAttributeValue(TT("threshold"),		0.0);
	setAttributeValue(TT("preamp"),			0.0);
	setAttributeValue(TT("postamp"),		0.0);
	setAttributeValue(TT("lookahead"),		100);
	setAttributeValue(TT("mode"),			TT("exponential"));
	setAttributeValue(TT("release"),		1000.0);
	setAttributeValue(TT("dcBlocker"),		*kTTBoolYes);
	setAttributeValue(TT("bypass"),			*kTTBoolNo);
	TTValue v;
	clear();
	setProcessMethod(processAudio);
}

TTLimiter::~TTLimiter()
{
	short i;

	for(i=0; i<maxNumChannels; i++)
		delete [] lookaheadBuffer[i];
	delete [] lookaheadBuffer;
	delete [] gain;
	delete dcBlocker;
	delete preamp;
}

// TODO: These message receiver args should be reversed -- this is a change that should be applied throughout TTBlue
TTErr TTLimiter::updateMaxNumChannels(const TTValue& oldMaxNumChannels)
{
	TTUInt16	channel;
	TTUInt16	numChannels = oldMaxNumChannels;

	if(lookaheadBuffer){
		for(channel=0; channel<numChannels; channel++)
			delete [] lookaheadBuffer[channel];
		delete [] lookaheadBuffer;
	}
	delete gain;

	gain = new TTSampleValue[maxBufferSize];
	lookaheadBuffer = new TTSampleVector[maxNumChannels];
	for(channel=0; channel<maxNumChannels; channel++)
		lookaheadBuffer[channel] = new TTSampleValue[maxBufferSize];

	clear();

	dcBlocker->setAttributeValue(TT("maxNumChannels"), maxNumChannels);
	preamp->setAttributeValue(TT("maxNumChannels"), maxNumChannels);

	return kTTErrNone;
}

TTErr TTLimiter::updateSr()
{
	setRecover();
	return kTTErrNone;
}

TTErr TTLimiter::setPreamp(const TTValue& newValue)
{
	return preamp->setAttributeValue(TT("gain"), newValue);
}

TTErr TTLimiter::getPreamp(TTValue& value)
{
	return preamp->getAttributeValue(TT("gain"), value);
}

TTErr TTLimiter::setPostamp(const TTValue& newValue)
{
	attrPostamp = dbToLinear(newValue);
	return kTTErrNone;
}

TTErr TTLimiter::getPostamp(TTValue& value)
{
	value = linearToDb(attrPostamp);
	return kTTErrNone;
}

TTErr TTLimiter::setThreshold(const TTValue& newValue)
{
	attrThreshold = dbToLinear(newValue);
	return kTTErrNone;
}

TTErr TTLimiter::getThreshold(TTValue& value)
{
	value = linearToDb(attrThreshold);
	return kTTErrNone;
}

TTErr TTLimiter::setLookahead(TTValue& newValue)
{
	attrLookahead = TTClip(TTUInt32(newValue), TTUInt32(1), maxBufferSize-1);
    lookaheadInv = 1.0 / TTFloat64(attrLookahead);
	return kTTErrNone;
}

TTErr TTLimiter::setRelease(TTValue& newValue)
{
	attrRelease = newValue;
	setRecover();
	return kTTErrNone;
}

TTErr TTLimiter::setMode(TTValue& newValue)
{
	attrMode = newValue;
	if(attrMode == TT("linear"))
		isLinear = true;
	else
		isLinear = false;
	setRecover();
	return kTTErrNone;
}

TTErr TTLimiter::setDCBlocker(TTValue& newValue)
{
	attrDCBlocker = newValue;
	return dcBlocker->setAttributeValue(TT("bypass"), !attrDCBlocker);
}

TTErr TTLimiter::clear()
{
	TTUInt32	i;
	TTUInt32	channel;

	for(i=0; i<maxBufferSize; i++){
		for(channel=0; channel < maxNumChannels; channel++)
    		lookaheadBuffer[channel][i] = 0.0;
    	gain[i] = 1.0;		// gain is shared across channels
    }

	lookaheadBufferIndex = 0;	// was bp
	last = 1.0;
	setRecover();

	dcBlocker->sendMessage(TT("clear"));
	return kTTErrNone;
}

// set variables related to the time it takes for the limiter to recover from a peak in the audio
// it is based on the release time attr, which is specified in seconds
void TTLimiter::setRecover()
{
	recover = 1000.0 / (attrRelease * sr);
	if(attrMode == TT("linear"))
		recover = recover * 0.5;
	else
		recover = recover * 0.707;
}

TTErr TTLimiter::processAudio(TTAudioSignalArrayPtr inputs, TTAudioSignalArrayPtr outputs)
{
	TTAudioSignal&	in = inputs->getSignal(0);
	TTAudioSignal&	out = outputs->getSignal(0);
	TTUInt16		vs = in.getVectorSize();
	TTUInt16		i, j;
	TTSampleValue	tempSample;
	TTSampleValue	hotSample;
	TTFloat64		maybe,
					curgain,
					newgain,
					inc,
					acc;
	TTInt16			flag,
					lookaheadBufferPlayback,
					ind;
	TTUInt16		numchannels = TTAudioSignal::getMinChannelCount(in, out);
	TTUInt16		channel;

	// Pre-Process the input
	dcBlocker->process(in, out);	// filter out DC-Offsets (unless it is bypassed)
	preamp->process(out, in);		// copy output back to input for the rest of this process

	for(i=0; i<vs; i++){
		hotSample = 0.0;

		// Analysis Stage ...
		for(channel=0; channel<numchannels; channel++){
			tempSample = in.sampleVectors[channel][i];
			lookaheadBuffer[channel][lookaheadBufferIndex] = tempSample * attrPostamp;
			tempSample = fabs(tempSample);
			if(tempSample > hotSample)
				hotSample = tempSample;
		}

		if(isLinear)
			tempSample = last + recover;
		else{
			if(last > 0.01)
				tempSample = last + recover * last;
			else
				tempSample = last + recover;
		}

		if(tempSample > attrThreshold)
			maybe = attrThreshold;
		else
			maybe = tempSample;
		gain[lookaheadBufferIndex] = maybe;

		lookaheadBufferPlayback = lookaheadBufferIndex - attrLookahead;
		if(lookaheadBufferPlayback < 0)
			lookaheadBufferPlayback += attrLookahead;

		// Process Stage ...
		for(channel=0; channel<numchannels; channel++){
			if(hotSample * maybe > attrThreshold){
				curgain = attrThreshold / hotSample;
				inc = (attrThreshold - curgain);
				acc = 0;
				flag = 0;
				for(j=0; flag==0 && j<attrLookahead; j++){
					ind = lookaheadBufferIndex - j;
					if(ind<0)
						ind += maxBufferSize;

					if(isLinear)
						newgain = curgain + inc * acc;
					else
						newgain = curgain + inc * (acc * acc);

					if(newgain < gain[ind])
						gain[ind] = newgain;
					else
						flag = 1;
					acc = acc + lookaheadInv;
				}
			}

			out.sampleVectors[channel][i] = lookaheadBuffer[channel][lookaheadBufferPlayback] * gain[lookaheadBufferPlayback];
			last = gain[lookaheadBufferIndex];
			lookaheadBufferIndex++;
			if(lookaheadBufferIndex >= attrLookahead)
				lookaheadBufferIndex = 0;
		}
	}
	return kTTErrNone;
}
<commit_msg>Fix for TTLimiter initialization where it was creating internal objects with an un-initialized variable for the number of channels.  This is likely the cause of a crash reported to me by Pascal.<commit_after>/*
 * TTBlue Limiter Object
 * Copyright © 2008, Timothy Place
 *
 * License: This code is licensed under the terms of the GNU LGPL
 * http://www.gnu.org/licenses/lgpl.html
 */

#include "TTLimiter.h"
#define thisTTClass TTLimiter

TTLimiter::TTLimiter(TTUInt16 newMaxNumChannels)
	: TTAudioObject("audio.limiter", newMaxNumChannels),
	lookaheadBuffer(NULL), lookaheadBufferIndex(0), gain(NULL), last(0.0), recover(0.0), maxBufferSize(512), attrMode(TT("exponential"))
{
	// register our attributes
	registerAttribute(TT("preamp"),		kTypeFloat64,	NULL,			(TTGetterMethod)&TTLimiter::getPreamp,		(TTSetterMethod)&TTLimiter::setPreamp);
	registerAttribute(TT("postamp"),	kTypeFloat64,	&attrPostamp,	(TTGetterMethod)&TTLimiter::getPostamp,		(TTSetterMethod)&TTLimiter::setPostamp);
	registerAttribute(TT("threshold"),	kTypeFloat64,	&attrThreshold,	(TTGetterMethod)&TTLimiter::getThreshold,	(TTSetterMethod)&TTLimiter::setThreshold);
	registerAttribute(TT("lookahead"),	kTypeUInt32,	&attrLookahead, (TTSetterMethod)&TTLimiter::setLookahead);
	registerAttribute(TT("release"),	kTypeFloat64,	&attrRelease,	(TTSetterMethod)&TTLimiter::setRelease);
	registerAttribute(TT("mode"),		kTypeSymbol,	&attrMode,		(TTSetterMethod)&TTLimiter::setMode);
	registerAttribute(TT("dcBlocker"),	kTypeBoolean,	&attrDCBlocker,	(TTSetterMethod)&TTLimiter::setDCBlocker);

	// register for notifications from the parent class so we can allocate memory as required
	registerMessageWithArgument(updateMaxNumChannels);
	// register for notifications from the parent class so we can update the release/recover values
	registerMessageSimple(updateSr);
	// clear the history
	registerMessageSimple(clear);

	dcBlocker = new TTDCBlock(newMaxNumChannels);
	preamp = new TTGain(newMaxNumChannels);

	// Set Defaults...
	setAttributeValue(TT("maxNumChannels"),	newMaxNumChannels);
	setAttributeValue(TT("threshold"),		0.0);
	setAttributeValue(TT("preamp"),			0.0);
	setAttributeValue(TT("postamp"),		0.0);
	setAttributeValue(TT("lookahead"),		100);
	setAttributeValue(TT("mode"),			TT("exponential"));
	setAttributeValue(TT("release"),		1000.0);
	setAttributeValue(TT("dcBlocker"),		*kTTBoolYes);
	setAttributeValue(TT("bypass"),			*kTTBoolNo);
	TTValue v;
	clear();
	setProcessMethod(processAudio);
}

TTLimiter::~TTLimiter()
{
	short i;

	for(i=0; i<maxNumChannels; i++)
		delete [] lookaheadBuffer[i];
	delete [] lookaheadBuffer;
	delete [] gain;
	delete dcBlocker;
	delete preamp;
}

// TODO: These message receiver args should be reversed -- this is a change that should be applied throughout TTBlue
TTErr TTLimiter::updateMaxNumChannels(const TTValue& oldMaxNumChannels)
{
	TTUInt16	channel;
	TTUInt16	numChannels = oldMaxNumChannels;

	if(lookaheadBuffer){
		for(channel=0; channel<numChannels; channel++)
			delete [] lookaheadBuffer[channel];
		delete [] lookaheadBuffer;
	}
	delete gain;

	gain = new TTSampleValue[maxBufferSize];
	lookaheadBuffer = new TTSampleVector[maxNumChannels];
	for(channel=0; channel<maxNumChannels; channel++)
		lookaheadBuffer[channel] = new TTSampleValue[maxBufferSize];

	clear();

	dcBlocker->setAttributeValue(TT("maxNumChannels"), maxNumChannels);
	preamp->setAttributeValue(TT("maxNumChannels"), maxNumChannels);

	return kTTErrNone;
}

TTErr TTLimiter::updateSr()
{
	setRecover();
	return kTTErrNone;
}

TTErr TTLimiter::setPreamp(const TTValue& newValue)
{
	return preamp->setAttributeValue(TT("gain"), newValue);
}

TTErr TTLimiter::getPreamp(TTValue& value)
{
	return preamp->getAttributeValue(TT("gain"), value);
}

TTErr TTLimiter::setPostamp(const TTValue& newValue)
{
	attrPostamp = dbToLinear(newValue);
	return kTTErrNone;
}

TTErr TTLimiter::getPostamp(TTValue& value)
{
	value = linearToDb(attrPostamp);
	return kTTErrNone;
}

TTErr TTLimiter::setThreshold(const TTValue& newValue)
{
	attrThreshold = dbToLinear(newValue);
	return kTTErrNone;
}

TTErr TTLimiter::getThreshold(TTValue& value)
{
	value = linearToDb(attrThreshold);
	return kTTErrNone;
}

TTErr TTLimiter::setLookahead(TTValue& newValue)
{
	attrLookahead = TTClip(TTUInt32(newValue), TTUInt32(1), maxBufferSize-1);
    lookaheadInv = 1.0 / TTFloat64(attrLookahead);
	return kTTErrNone;
}

TTErr TTLimiter::setRelease(TTValue& newValue)
{
	attrRelease = newValue;
	setRecover();
	return kTTErrNone;
}

TTErr TTLimiter::setMode(TTValue& newValue)
{
	attrMode = newValue;
	if(attrMode == TT("linear"))
		isLinear = true;
	else
		isLinear = false;
	setRecover();
	return kTTErrNone;
}

TTErr TTLimiter::setDCBlocker(TTValue& newValue)
{
	attrDCBlocker = newValue;
	return dcBlocker->setAttributeValue(TT("bypass"), !attrDCBlocker);
}

TTErr TTLimiter::clear()
{
	TTUInt32	i;
	TTUInt32	channel;

	for(i=0; i<maxBufferSize; i++){
		for(channel=0; channel < maxNumChannels; channel++)
    		lookaheadBuffer[channel][i] = 0.0;
    	gain[i] = 1.0;		// gain is shared across channels
    }

	lookaheadBufferIndex = 0;	// was bp
	last = 1.0;
	setRecover();

	dcBlocker->sendMessage(TT("clear"));
	return kTTErrNone;
}

// set variables related to the time it takes for the limiter to recover from a peak in the audio
// it is based on the release time attr, which is specified in seconds
void TTLimiter::setRecover()
{
	recover = 1000.0 / (attrRelease * sr);
	if(attrMode == TT("linear"))
		recover = recover * 0.5;
	else
		recover = recover * 0.707;
}

TTErr TTLimiter::processAudio(TTAudioSignalArrayPtr inputs, TTAudioSignalArrayPtr outputs)
{
	TTAudioSignal&	in = inputs->getSignal(0);
	TTAudioSignal&	out = outputs->getSignal(0);
	TTUInt16		vs = in.getVectorSize();
	TTUInt16		i, j;
	TTSampleValue	tempSample;
	TTSampleValue	hotSample;
	TTFloat64		maybe,
					curgain,
					newgain,
					inc,
					acc;
	TTInt16			flag,
					lookaheadBufferPlayback,
					ind;
	TTUInt16		numchannels = TTAudioSignal::getMinChannelCount(in, out);
	TTUInt16		channel;

	// Pre-Process the input
	dcBlocker->process(in, out);	// filter out DC-Offsets (unless it is bypassed)
	preamp->process(out, in);		// copy output back to input for the rest of this process

	for(i=0; i<vs; i++){
		hotSample = 0.0;

		// Analysis Stage ...
		for(channel=0; channel<numchannels; channel++){
			tempSample = in.sampleVectors[channel][i];
			lookaheadBuffer[channel][lookaheadBufferIndex] = tempSample * attrPostamp;
			tempSample = fabs(tempSample);
			if(tempSample > hotSample)
				hotSample = tempSample;
		}

		if(isLinear)
			tempSample = last + recover;
		else{
			if(last > 0.01)
				tempSample = last + recover * last;
			else
				tempSample = last + recover;
		}

		if(tempSample > attrThreshold)
			maybe = attrThreshold;
		else
			maybe = tempSample;
		gain[lookaheadBufferIndex] = maybe;

		lookaheadBufferPlayback = lookaheadBufferIndex - attrLookahead;
		if(lookaheadBufferPlayback < 0)
			lookaheadBufferPlayback += attrLookahead;

		// Process Stage ...
		for(channel=0; channel<numchannels; channel++){
			if(hotSample * maybe > attrThreshold){
				curgain = attrThreshold / hotSample;
				inc = (attrThreshold - curgain);
				acc = 0;
				flag = 0;
				for(j=0; flag==0 && j<attrLookahead; j++){
					ind = lookaheadBufferIndex - j;
					if(ind<0)
						ind += maxBufferSize;

					if(isLinear)
						newgain = curgain + inc * acc;
					else
						newgain = curgain + inc * (acc * acc);

					if(newgain < gain[ind])
						gain[ind] = newgain;
					else
						flag = 1;
					acc = acc + lookaheadInv;
				}
			}

			out.sampleVectors[channel][i] = lookaheadBuffer[channel][lookaheadBufferPlayback] * gain[lookaheadBufferPlayback];
			last = gain[lookaheadBufferIndex];
			lookaheadBufferIndex++;
			if(lookaheadBufferIndex >= attrLookahead)
				lookaheadBufferIndex = 0;
		}
	}
	return kTTErrNone;
}
<|endoftext|>


<commit_before>// main.cpp

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

#include "metasorter.h"

#include <boost/foreach.hpp>
#include <boost/property_tree/ptree.hpp>
#include <boost/property_tree/info_parser.hpp>

void generate_skeleton_config();

using namespace std;
using namespace boost::filesystem;

int main(int argc, char* argv[])
{
	std::cout << "Metasort 1.4.5" << std::endl << std::endl;
	//std::cout << "Copyright 2013 Eric Griffin" << std::endl << std::endl;

	int err = 0;
	int ok_to_run = 0;
	int required_flags = 0;
	int config_num = 0;
	int input_file_process = 0;
	int input_file_num = 0;
	char* input_file = (char*)malloc(sizeof(char[255][255]));
	char* config_file = (char*)malloc(sizeof(char[255][255]));
	boost::property_tree::ptree pt[255];

	if (argc > 1)
	{
		for (int i = 1; i < argc; i++)
		{
			if (strcmp(argv[i], "-g") == 0 )
			{
				generate_skeleton_config();
				exit(0);
            }

			if (strcmp(argv[i], "-?") == 0 || strcmp(argv[i], "-h") == 0 || strcmp(argv[i], "/h") == 0)
			{
				std::cout << "Usage: metasort -c <config file> [-i <filename>] [-g]" << std::endl;
				std::cout << "-c <config file>  --  specify configuration file to run" << std::endl;
				std::cout << "-i <filename>  --  specify a single file to process (requires -c)" << std::endl;
				std::cout << "-g  --  create a skeleton configuration file in the current directory" << std::endl << std::endl;
				exit(0);
            }

			if (i + 1 != argc)
			{
                if (strcmp(argv[i], "-c") == 0)
				{
                    strcpy(&config_file[config_num], argv[i + 1]);
					config_num++;
					required_flags++;
                }
				if (strcmp(argv[i], "-i") == 0)
				{
					input_file_process = 1;
                    strcpy(&input_file[input_file_num], argv[i + 1]);
					input_file_num++;
                }
			}
		}
		if(required_flags > 0)
		{
			ok_to_run = 1;
		}
	}

	if(ok_to_run)
	{
		for(int q = 0; q < config_num; q++)
		{
			boost::property_tree::info_parser::read_info(&config_file[q], pt[q]);

			// check for folders entry
			optional<const boost::property_tree::ptree&> pt_check_existence;
			pt_check_existence = pt[q].get_child_optional("folders");

			if(input_file_process == 0)  // if processing folders from config
			{
				if(pt_check_existence)
				{
					BOOST_FOREACH(boost::property_tree::ptree::value_type &v, pt[q].get_child("folders"))
					{
						int recurse = 0;
						std::cout << "Searching Root Folder: " << v.first.data() << std::endl;
						if(strcmp(v.second.data().c_str(), "R") == 0)
						{
							recurse = 1;
						}
						metasorter sorter((char*)v.first.data(), pt[q]);
						sorter.traverse_directory(recurse);
						sorter.tp.wait();
					}
					std::cout << endl << "Finished." << std::endl;
				}
				else
				{
					cout << endl << "No folders defined in config file - aborting" << std::endl;
					exit(0);
				}
			}
			else  // processing files from argv[]
			{
				for(int input_file_counter = 0; input_file_counter < input_file_num; input_file_counter++)
				{
					metasorter sorter(&input_file[input_file_counter], pt[q]);
					sorter.process_file();
				}
				std::cout << endl << "Finished." << std::endl;
			}
		}
	}
	else
	{
		std::cout << "Usage: metasort -c <config file> [-i <filename>] [-g]" << std::endl << std::endl;
	}

	delete input_file;
	delete config_file;
	return err;
}

void generate_skeleton_config()
{
	boost::filesystem::path working_path_raw(boost::filesystem::current_path());
	std::string *config = new std::string();
	std::string *working_path = new std::string(working_path_raw.string().c_str());

	//change back-slashes to forward-slashes
	std::replace(working_path->begin(), working_path->end(), '\\', '/');

	config->append("folders\n{\n\t;\"");
	config->append(working_path->c_str());
	config->append("/\" R\n}\n\nextensions\n{\n");
	config->append("\t[REGEX].*");
	config->append("\n}\n\nconfig\n{\n");
	config->append("\tlogfile \"");
	config->append(working_path->c_str());
	config->append("/metasort.log\"");
	config->append("\n}\n\nrules\n{\n");
	config->append("\tRule1_list \"");
	config->append(working_path->c_str());
	config->append("/metasort_list.txt\"\n\t{\n");
	config->append("\t\tGeneral_0\n\t\t{\n");
	config->append("\t\t\tfile_size > 50\n\t\t}\n");
	config->append("\n\t}\n}\n");

	std::ofstream f;
	std::string *skeleton_config_file = new std::string(working_path_raw.string().c_str());
	skeleton_config_file->append("/metasort_config.ini");
	std::cout << "Generating skeleton config file: " << skeleton_config_file->c_str() << std::endl << std::endl;
    f.open(skeleton_config_file->c_str(), std::ios::out);
	f << config->c_str();
	f.close();
}
<commit_msg>moved more data from stack to heap. bug fix in memory clean-up.<commit_after>// main.cpp

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

#include "metasorter.h"

#include <boost/foreach.hpp>
#include <boost/property_tree/ptree.hpp>
#include <boost/property_tree/info_parser.hpp>

void generate_skeleton_config();

using namespace std;
using namespace boost::filesystem;

int main(int argc, char* argv[])
{
	std::cout << "Metasort 1.4.5" << std::endl << std::endl;
	//std::cout << "Copyright 2013 Eric Griffin" << std::endl << std::endl;

	int err = 0;
	int ok_to_run = 0;
	int required_flags = 0;
	int config_num = 0;
	int input_file_process = 0;
	int input_file_num = 0;
	char* input_file = (char*)malloc(sizeof(char[255][255]));
	char* config_file = (char*)malloc(sizeof(char[255][255]));
	boost::property_tree::ptree* pt = new boost::property_tree::ptree[255];

	if (argc > 1)
	{
		for (int i = 1; i < argc; i++)
		{
			if (strcmp(argv[i], "-g") == 0 )
			{
				generate_skeleton_config();
				exit(0);
            }

			if (strcmp(argv[i], "-?") == 0 || strcmp(argv[i], "-h") == 0 || strcmp(argv[i], "/h") == 0)
			{
				std::cout << "Usage: metasort -c <config file> [-i <filename>] [-g]" << std::endl;
				std::cout << "-c <config file>  --  specify configuration file to run" << std::endl;
				std::cout << "-i <filename>  --  specify a single file to process (requires -c)" << std::endl;
				std::cout << "-g  --  create a skeleton configuration file in the current directory" << std::endl << std::endl;
				exit(0);
            }

			if (i + 1 != argc)
			{
                if (strcmp(argv[i], "-c") == 0)
				{
                    strcpy(&config_file[config_num], argv[i + 1]);
					config_num++;
					required_flags++;
                }
				if (strcmp(argv[i], "-i") == 0)
				{
					input_file_process = 1;
                    strcpy(&input_file[input_file_num], argv[i + 1]);
					input_file_num++;
                }
			}
		}
		if(required_flags > 0)
		{
			ok_to_run = 1;
		}
	}

	if(ok_to_run)
	{
		for(int q = 0; q < config_num; q++)
		{
			boost::property_tree::info_parser::read_info(&config_file[q], pt[q]);

			// check for folders entry
			optional<const boost::property_tree::ptree&> pt_check_existence;
			pt_check_existence = pt[q].get_child_optional("folders");

			if(input_file_process == 0)  // if processing folders from config
			{
				if(pt_check_existence)
				{
					BOOST_FOREACH(boost::property_tree::ptree::value_type &v, pt[q].get_child("folders"))
					{
						int recurse = 0;
						std::cout << "Searching Root Folder: " << v.first.data() << std::endl;
						if(strcmp(v.second.data().c_str(), "R") == 0)
						{
							recurse = 1;
						}
						metasorter sorter((char*)v.first.data(), pt[q]);
						sorter.traverse_directory(recurse);
						sorter.tp.wait();
					}
					std::cout << endl << "Finished." << std::endl;
				}
				else
				{
					cout << endl << "No folders defined in config file - aborting" << std::endl;
					exit(0);
				}
			}
			else  // processing files from argv[]
			{
				for(int input_file_counter = 0; input_file_counter < input_file_num; input_file_counter++)
				{
					metasorter sorter(&input_file[input_file_counter], pt[q]);
					sorter.process_file();
				}
				std::cout << endl << "Finished." << std::endl;
			}
		}
	}
	else
	{
		std::cout << "Usage: metasort -c <config file> [-i <filename>] [-g]" << std::endl << std::endl;
	}

	delete[] pt;
	delete[] input_file;
	delete[] config_file;
	return err;
}

void generate_skeleton_config()
{
	boost::filesystem::path working_path_raw(boost::filesystem::current_path());
	std::string *config = new std::string();
	std::string *working_path = new std::string(working_path_raw.string().c_str());

	//change back-slashes to forward-slashes
	std::replace(working_path->begin(), working_path->end(), '\\', '/');

	config->append("folders\n{\n\t;\"");
	config->append(working_path->c_str());
	config->append("/\" R\n}\n\nextensions\n{\n");
	config->append("\t[REGEX].*");
	config->append("\n}\n\nconfig\n{\n");
	config->append("\tlogfile \"");
	config->append(working_path->c_str());
	config->append("/metasort.log\"");
	config->append("\n}\n\nrules\n{\n");
	config->append("\tRule1_list \"");
	config->append(working_path->c_str());
	config->append("/metasort_list.txt\"\n\t{\n");
	config->append("\t\tGeneral_0\n\t\t{\n");
	config->append("\t\t\tfile_size > 50\n\t\t}\n");
	config->append("\n\t}\n}\n");

	std::ofstream f;
	std::string *skeleton_config_file = new std::string(working_path_raw.string().c_str());
	skeleton_config_file->append("/metasort_config.ini");
	std::cout << "Generating skeleton config file: " << skeleton_config_file->c_str() << std::endl << std::endl;
    f.open(skeleton_config_file->c_str(), std::ios::out);
	f << config->c_str();
	f.close();
}
<|endoftext|>


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

#include "mbed.h"
#include "ble/BLE.h"
#include "ble-blocktransfer/BlockTransferService.h"
#include "voytalk/Voytalk.h"

/*****************************************************************************/
/* Configuration                                                             */
/*****************************************************************************/

// set device name
const char DEVICE_NAME[] = "Testoy";

// set TX power
#ifndef CFG_BLE_TX_POWER_LEVEL
#define CFG_BLE_TX_POWER_LEVEL 0
#endif

// control debug output
#if 1
#define DEBUGOUT(...) { printf(__VA_ARGS__); }
#else
#define DEBUGOUT(...) /* nothing */
#endif // DEBUGOUT

/*****************************************************************************/

/* Voytalk short UUID */
const UUID uuid(0xFE8E);

/* Voytalk states */
typedef enum {
    FLAG_CONNECTED      = 0x01,
    FLAG_PROVISIONED    = 0x02,
} flags_t;

static volatile uint8_t state;

/*****************************************************************************/
/* Global variables used by the test app                                     */
/*****************************************************************************/

// BLE_API ble device
BLE ble;

// Transfer large blocks of data on platforms without Fragmentation-And-Recombination
BlockTransferService bts;

// Voytalk handling
VoytalkRouter router(DEVICE_NAME);

// buffer for sending and receiving data
SharedPointer<Block> writeBlock;
uint8_t readBuffer[1000];
BlockStatic readBlock(readBuffer, sizeof(readBuffer));

// wifi parameters
std::string ssid_string;
std::string key_string;

// Compatibility function
void signalReady();

/*****************************************************************************/
/* Functions for handling debug output                                       */
/*****************************************************************************/

/*
    Functions called when BLE device connects and disconnects.
*/
void whenConnected(const Gap::ConnectionCallbackParams_t* params)
{
    (void) params;
    DEBUGOUT("main: Connected: %d %d %d\r\n", params->connectionParams->minConnectionInterval,
                                              params->connectionParams->maxConnectionInterval,
                                              params->connectionParams->slaveLatency);

    // change state in main application
    state |= FLAG_CONNECTED;

    // change state inside Voytalk hub
    router.setStateMask(state);
}

void whenDisconnected(const Gap::DisconnectionCallbackParams_t*)
{
    DEBUGOUT("main: Disconnected!\r\n");
    DEBUGOUT("main: Restarting the advertising process\r\n");

    ble.gap().startAdvertising();

    // change state in main application
    state &= ~FLAG_CONNECTED;

    // change state inside Voytalk hub
    router.setStateMask(state);
}

/*****************************************************************************/
/* BlockTransfer callbacks for sending and receiving data                    */
/*****************************************************************************/

/*
    Function called when signaling client that new data is ready to be read.
*/
void blockServerSendNotification()
{
    DEBUGOUT("main: notify read updated\r\n");
    bts.updateCharacteristicValue((uint8_t*)"", 0);
}

/*
    Function called when device receives a read request over BLE.
*/
SharedPointer<Block> blockServerReadHandler(uint32_t offset)
{
    DEBUGOUT("main: block read\r\n");
    (void) offset;

    return SharedPointer<Block>(new BlockStatic(readBlock));
}

/*
    Function called when data has been written over BLE.
*/
void blockServerWriteHandler(SharedPointer<Block> block)
{
    DEBUGOUT("main: block write\r\n");

    /*
        Process received data, assuming it is CBOR encoded.
        Any output generated will be written to the readBlock.
    */
    router.processCBOR((BlockStatic*) block.get(), &readBlock);

    /*
        If the readBlock length is non-zero it means a reply has been generated.
    */
    if (readBlock.getLength() > 0)
    {
        signalReady();
    }
}

/*****************************************************************************/
/* Voytalk Wifi example                                                      */
/*****************************************************************************/

/*
    Callback function for constructing wifi intent.
*/
void wifiIntentConstruction(VTRequest& req, VTResponse& res)
{
    DEBUGOUT("main: wifi intent construction\r\n");
    /* create intent using generated endpoint and constraint set */
    VTIntent intent("com.arm.connectivity.wifi");
    intent.knownParameters("/networks")
        .endpoint("/wifi");

    res.write(intent);
}

/*****************************************************************************/
/* Reset device example                                                      */
/*****************************************************************************/

void resetIntentConstruction(VTRequest& req, VTResponse& res)
{
    DEBUGOUT("main: reset intent construction\r\n");

    /* create intent using generated endpoint and constraint set */
    VTIntent intent("com.arm.reset");
    intent.endpoint("/reset");
    res.write(intent);
}

/*****************************************************************************/
/* Voytalk complex example                                                   */
/*****************************************************************************/

/*
    Callback functions for the example intent.
*/
void exampleIntentConstruction(VTRequest& req, VTResponse& res)
{
    DEBUGOUT("main: complex example intent construction\r\n");

    /* create intent */
    VTIntent intent("com.arm.examples.complex");
    intent.endpoint("/examples/complex");

    res.write(intent);
}

/*****************************************************************************/
/* Voytalk custom example                                                    */
/*****************************************************************************/

void customIntentConstruction(VTRequest& req, VTResponse& res)
{
    DEBUGOUT("main: custom intent construction\r\n");
    /* create intent using generated endpoint and constraint set */
    VTIntent intent("com.arm.examples.custom");
    intent.endpoint("/custom")
        .constraints()
            .title("Hello!")
            .description("This is the description")
            .addConstraint("test",
                VTConstraint(VTConstraint::TypeString)
                    .title("Test")
                    .defaultValue("default goes here")
            )
            .addConstraint("test2",
                VTConstraint(VTConstraint::TypeString)
                    .title("Other test")
                    .defaultValue("default goes here")
            );
    res.write(intent);
}

/*****************************************************************************/
/* Middleware for actually doing stuff                                       */
/*****************************************************************************/

void printInvocation(VTRequest& req, VTResponse& res, VoytalkRouter::done_t done)
{
    VTIntentInvocation invocation(req.getBody());
    invocation.getParameters().print();
}

void saveWifi(VTRequest& req, VTResponse& res, VoytalkRouter::done_t done)
{
    DEBUGOUT("main: saving wifi details\r\n");

    VTIntentInvocation invocation(req.getBody());

    invocation.getParameters().find("ssid").getString(ssid_string);
    invocation.getParameters().find("key").getString(key_string);

    // change state in main application
    state |= FLAG_PROVISIONED;

    // change state inside Voytalk hub
    router.setStateMask(state);
}

void resetDevice(VTRequest& req, VTResponse& res, VoytalkRouter::done_t done)
{
    DEBUGOUT("main: reset device\r\n");

    ssid_string = "";
    key_string = "";

    // change state in main application
    state &= ~FLAG_PROVISIONED;

    // change state inside Voytalk hub
    router.setStateMask(state);
}

void sendSuccess(VTRequest& req, VTResponse& res, VoytalkRouter::done_t done)
{
    VTIntentInvocation invocation(req.getBody());
    VTCoda coda(invocation.getID());
    coda.success(true);
    res.write(coda);
    done(200);
}

void networkList(VTRequest& req, VTResponse& res, VoytalkRouter::done_t done)
{
    DEBUGOUT("listing network resources");

    VoytalkKnownParameters parameters(res, 2);

    parameters.parameter("com.arm.connectivity.wifi", 50)
        .map(2)
            .key("ssid").value("miWifi")
            .key("key").value("supersecurepassword");

    parameters.parameter("com.arm.connectivity.wifi", 20)
        .map(2)
            .key("ssid").value("yoWifi")
            .key("key").value("securepasswordinit");

    done(200);
}

/*****************************************************************************/
/* main                                                                      */
/*****************************************************************************/

void app_start(int, char *[])
{
    /*
        Register Voytalk intents in the hub.

        First parameter is the callback function for intent generation.
        Second parameter is a bitmap for grouping intents together.
    */

    // Wifi provisioning intent
    router.registerIntent(wifiIntentConstruction,
                          FLAG_CONNECTED | FLAG_PROVISIONED);

    // reset intent
    router.registerIntent(resetIntentConstruction,
                          FLAG_PROVISIONED);

    // custom intent
    //router.registerIntent(customIntentConstruction,
    //                     FLAG_CONNECTED | FLAG_PROVISIONED);

    // example intent
    router.registerIntent(exampleIntentConstruction,
                         FLAG_CONNECTED | FLAG_PROVISIONED);

    /*
        Set the current state mask.

        Mask is AND'ed with each intent's bitmap and only intents with non-zero
        results are displayed and can be invoked.
    */
    router.setStateMask(0);

    /*
        Define some resource callbacks
    */

    router.get("/networks",          networkList, NULL);
    router.post("/wifi",             printInvocation, saveWifi,    sendSuccess, NULL);
    router.post("/reset",            printInvocation, resetDevice, sendSuccess, NULL);
    router.post("/custom",           printInvocation,              sendSuccess, NULL);
    router.post("/examples/complex", printInvocation,              sendSuccess, NULL);

    /*************************************************************************/
    /*************************************************************************/
    /* bluetooth le */
    ble.init();

    // status callback functions
    ble.gap().onConnection(whenConnected);
    ble.gap().onDisconnection(whenDisconnected);

    /* construct advertising beacon */
    ble.gap().accumulateAdvertisingPayload(GapAdvertisingData::BREDR_NOT_SUPPORTED|GapAdvertisingData::LE_GENERAL_DISCOVERABLE);
    ble.gap().accumulateAdvertisingPayload(GapAdvertisingData::SHORTENED_LOCAL_NAME, (const uint8_t *) DEVICE_NAME, sizeof(DEVICE_NAME) - 1);
    ble.gap().accumulateAdvertisingPayload(GapAdvertisingData::COMPLETE_LIST_16BIT_SERVICE_IDS, uuid.getBaseUUID(), uuid.getLen());
    ble.gap().accumulateAdvertisingPayloadTxPower(CFG_BLE_TX_POWER_LEVEL);

    ble.gap().setAdvertisingType(GapAdvertisingParams::ADV_CONNECTABLE_UNDIRECTED);
    ble.gap().setAdvertisingInterval(1000); /* 1s; in multiples of 0.625ms. */

    // set TX power
    ble.gap().setTxPower(CFG_BLE_TX_POWER_LEVEL);

    // Apple uses device name instead of beacon name
    ble.gap().setDeviceName((const uint8_t*) DEVICE_NAME);

    /*************************************************************************/
    /*************************************************************************/
    // setup block transfer service

    // add service using ble device, responding to uuid, and without encryption
    bts.init(uuid, SecurityManager::SECURITY_MODE_ENCRYPTION_OPEN_LINK);

    // set callback functions for the BlockTransfer service
    bts.setWriteAuthorizationCallback(blockServerWriteHandler);
    bts.setReadAuthorizationCallback(blockServerReadHandler);

    // ble setup complete - start advertising
    ble.gap().startAdvertising();

    printf("Voytalk Test: %s %s\r\n", __DATE__, __TIME__);
}

/*****************************************************************************/
/* Compatibility                                                             */
/*****************************************************************************/

#if defined(YOTTA_MINAR_VERSION_STRING)
/*********************************************************/
/* Build for mbed OS                                     */
/*********************************************************/

void signalReady()
{
    minar::Scheduler::postCallback(blockServerSendNotification);
}

#else
/*********************************************************/
/* Build for mbed Classic                                */
/*********************************************************/

bool sendNotification = false;

void signalReady()
{
    sendNotification = true;
}

int main(void)
{
    app_start(0, NULL);

    for(;;)
    {
        // send notification outside of interrupt context
        if (sendNotification)
        {
            sendNotification = false;
            blockServerSendNotification();
        }

        ble.waitForEvent();
    }
}
#endif

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

#include "mbed.h"
#include "ble/BLE.h"
#include "ble-blocktransfer/BlockTransferService.h"
#include "voytalk/Voytalk.h"

/*****************************************************************************/
/* Configuration                                                             */
/*****************************************************************************/

// set device name
const char DEVICE_NAME[] = "Testoy";

// set TX power
#ifndef CFG_BLE_TX_POWER_LEVEL
#define CFG_BLE_TX_POWER_LEVEL 0
#endif

// control debug output
#if 1
#define DEBUGOUT(...) { printf(__VA_ARGS__); }
#else
#define DEBUGOUT(...) /* nothing */
#endif // DEBUGOUT

/*****************************************************************************/

/* Voytalk short UUID */
const UUID uuid(0xFE8E);

/* Voytalk states */
typedef enum {
    FLAG_CONNECTED      = 0x01,
    FLAG_PROVISIONED    = 0x02,
} flags_t;

static volatile uint8_t state;

/*****************************************************************************/
/* Global variables used by the test app                                     */
/*****************************************************************************/

// BLE_API ble device
BLE ble;

// Transfer large blocks of data on platforms without Fragmentation-And-Recombination
BlockTransferService bts;

// Voytalk handling
VoytalkRouter router(DEVICE_NAME);

// buffer for sending and receiving data
SharedPointer<Block> writeBlock;
uint8_t readBuffer[1000];
BlockStatic readBlock(readBuffer, sizeof(readBuffer));

// wifi parameters
std::string ssid_string;
std::string key_string;

// Compatibility function
void signalReady();

/*****************************************************************************/
/* Functions for handling debug output                                       */
/*****************************************************************************/

/*
    Functions called when BLE device connects and disconnects.
*/
void whenConnected(const Gap::ConnectionCallbackParams_t* params)
{
    (void) params;
    DEBUGOUT("main: Connected: %d %d %d\r\n", params->connectionParams->minConnectionInterval,
                                              params->connectionParams->maxConnectionInterval,
                                              params->connectionParams->slaveLatency);

    // change state in main application
    state |= FLAG_CONNECTED;

    // change state inside Voytalk hub
    router.setStateMask(state);
}

void whenDisconnected(const Gap::DisconnectionCallbackParams_t*)
{
    DEBUGOUT("main: Disconnected!\r\n");
    DEBUGOUT("main: Restarting the advertising process\r\n");

    ble.gap().startAdvertising();

    // change state in main application
    state &= ~FLAG_CONNECTED;

    // change state inside Voytalk hub
    router.setStateMask(state);
}

/*****************************************************************************/
/* BlockTransfer callbacks for sending and receiving data                    */
/*****************************************************************************/

/*
    Function called when signaling client that new data is ready to be read.
*/
void blockServerSendNotification()
{
    DEBUGOUT("main: notify read updated\r\n");
    bts.updateCharacteristicValue((uint8_t*)"", 0);
}

/*
    Function called when device receives a read request over BLE.
*/
SharedPointer<Block> blockServerReadHandler(uint32_t offset)
{
    DEBUGOUT("main: block read\r\n");
    (void) offset;

    return SharedPointer<Block>(new BlockStatic(readBlock));
}

/*
    Function called when data has been written over BLE.
*/
void blockServerWriteHandler(SharedPointer<Block> block)
{
    DEBUGOUT("main: block write\r\n");

    /*
        Process received data, assuming it is CBOR encoded.
        Any output generated will be written to the readBlock.
    */
    router.processCBOR((BlockStatic*) block.get(), &readBlock);

    /*
        If the readBlock length is non-zero it means a reply has been generated.
    */
    if (readBlock.getLength() > 0)
    {
        signalReady();
    }
}

/*****************************************************************************/
/* Voytalk Wifi example                                                      */
/*****************************************************************************/

/*
    Callback function for constructing wifi intent.
*/
void wifiIntentConstruction(VTRequest& req, VTResponse& res)
{
    DEBUGOUT("main: wifi intent construction\r\n");
    /* create intent using generated endpoint and constraint set */
    VTIntent intent("com.arm.connectivity.wifi");
    intent.knownParameters("/networks")
        .endpoint("/wifi");

    res.write(intent);
}

/*****************************************************************************/
/* Reset device example                                                      */
/*****************************************************************************/

void resetIntentConstruction(VTRequest& req, VTResponse& res)
{
    DEBUGOUT("main: reset intent construction\r\n");

    /* create intent using generated endpoint and constraint set */
    VTIntent intent("com.arm.reset");
    intent.endpoint("/reset");
    res.write(intent);
}

/*****************************************************************************/
/* Voytalk complex example                                                   */
/*****************************************************************************/

/*
    Callback functions for the example intent.
*/
void exampleIntentConstruction(VTRequest& req, VTResponse& res)
{
    DEBUGOUT("main: complex example intent construction\r\n");

    /* create intent */
    VTIntent intent("com.arm.examples.complex");
    intent.endpoint("/examples/complex");

    res.write(intent);
}

/*****************************************************************************/
/* Voytalk custom example                                                    */
/*****************************************************************************/

void customIntentConstruction(VTRequest& req, VTResponse& res)
{
    DEBUGOUT("main: custom intent construction\r\n");
    /* create intent using generated endpoint and constraint set */
    VTIntent intent("com.arm.examples.custom");
    intent.endpoint("/custom")
        .constraints()
            .title("Hello!")
            .description("This is the description")
            .addConstraint("test",
                VTConstraint(VTConstraint::TypeString)
                    .title("Test")
                    .defaultValue("default goes here")
            )
            .addConstraint("test2",
                VTConstraint(VTConstraint::TypeString)
                    .title("Other test")
                    .defaultValue("default goes here")
            );
    res.write(intent);
}

/*****************************************************************************/
/* Middleware for actually doing stuff                                       */
/*****************************************************************************/

void printInvocation(VTRequest& req, VTResponse& res, Next&::next_t next)
{
    VTIntentInvocation invocation(req.getBody());
    invocation.getParameters().print();
    next();
}

void saveWifi(VTRequest& req, VTResponse& res, VoytalkRouter::next_t next)
{
    DEBUGOUT("main: saving wifi details\r\n");

    VTIntentInvocation invocation(req.getBody());

    invocation.getParameters().find("ssid").getString(ssid_string);
    invocation.getParameters().find("key").getString(key_string);

    // change state in main application
    state |= FLAG_PROVISIONED;

    // change state inside Voytalk hub
    router.setStateMask(state);

    next();
}

void resetDevice(VTRequest& req, VTResponse& res, VoytalkRouter::next_t next)
{
    DEBUGOUT("main: reset device\r\n");

    ssid_string = "";
    key_string = "";

    // change state in main application
    state &= ~FLAG_PROVISIONED;

    // change state inside Voytalk hub
    router.setStateMask(state);

    next();
}

void sendSuccess(VTRequest& req, VTResponse& res, VoytalkRouter::next_t next)
{
    VTIntentInvocation invocation(req.getBody());
    VTCoda coda(invocation.getID());
    coda.success(true);
    res.write(coda);
    next(200);
}

void networkList(VTRequest& req, VTResponse& res, VoytalkRouter::next_t next)
{
    DEBUGOUT("listing network resources");

    VoytalkKnownParameters parameters(res, 2);

    parameters.parameter("com.arm.connectivity.wifi", 50)
        .map(2)
            .key("ssid").value("miWifi")
            .key("key").value("supersecurepassword");

    parameters.parameter("com.arm.connectivity.wifi", 20)
        .map(2)
            .key("ssid").value("yoWifi")
            .key("key").value("securepasswordinit");

    next(200);
}

/*****************************************************************************/
/* main                                                                      */
/*****************************************************************************/

void app_start(int, char *[])
{
    /*
        Register Voytalk intents in the hub.

        First parameter is the callback function for intent generation.
        Second parameter is a bitmap for grouping intents together.
    */

    // Wifi provisioning intent
    router.registerIntent(wifiIntentConstruction,
                          FLAG_CONNECTED | FLAG_PROVISIONED);

    // reset intent
    router.registerIntent(resetIntentConstruction,
                          FLAG_PROVISIONED);

    // custom intent
    //router.registerIntent(customIntentConstruction,
    //                     FLAG_CONNECTED | FLAG_PROVISIONED);

    // example intent
    router.registerIntent(exampleIntentConstruction,
                         FLAG_CONNECTED | FLAG_PROVISIONED);

    /*
        Set the current state mask.

        Mask is AND'ed with each intent's bitmap and only intents with non-zero
        results are displayed and can be invoked.
    */
    router.setStateMask(0);

    /*
        Define some resource callbacks
    */

    router.get("/networks",          networkList,                               NULL);
    router.post("/wifi",             printInvocation, saveWifi,    sendSuccess, NULL);
    router.post("/reset",            printInvocation, resetDevice, sendSuccess, NULL);
    router.post("/custom",           printInvocation,              sendSuccess, NULL);
    router.post("/examples/complex", printInvocation,              sendSuccess, NULL);

    /*************************************************************************/
    /*************************************************************************/
    /* bluetooth le */
    ble.init();

    // status callback functions
    ble.gap().onConnection(whenConnected);
    ble.gap().onDisconnection(whenDisconnected);

    /* construct advertising beacon */
    ble.gap().accumulateAdvertisingPayload(GapAdvertisingData::BREDR_NOT_SUPPORTED|GapAdvertisingData::LE_GENERAL_DISCOVERABLE);
    ble.gap().accumulateAdvertisingPayload(GapAdvertisingData::SHORTENED_LOCAL_NAME, (const uint8_t *) DEVICE_NAME, sizeof(DEVICE_NAME) - 1);
    ble.gap().accumulateAdvertisingPayload(GapAdvertisingData::COMPLETE_LIST_16BIT_SERVICE_IDS, uuid.getBaseUUID(), uuid.getLen());
    ble.gap().accumulateAdvertisingPayloadTxPower(CFG_BLE_TX_POWER_LEVEL);

    ble.gap().setAdvertisingType(GapAdvertisingParams::ADV_CONNECTABLE_UNDIRECTED);
    ble.gap().setAdvertisingInterval(1000); /* 1s; in multiples of 0.625ms. */

    // set TX power
    ble.gap().setTxPower(CFG_BLE_TX_POWER_LEVEL);

    // Apple uses device name instead of beacon name
    ble.gap().setDeviceName((const uint8_t*) DEVICE_NAME);

    /*************************************************************************/
    /*************************************************************************/
    // setup block transfer service

    // add service using ble device, responding to uuid, and without encryption
    bts.init(uuid, SecurityManager::SECURITY_MODE_ENCRYPTION_OPEN_LINK);

    // set callback functions for the BlockTransfer service
    bts.setWriteAuthorizationCallback(blockServerWriteHandler);
    bts.setReadAuthorizationCallback(blockServerReadHandler);

    // ble setup complete - start advertising
    ble.gap().startAdvertising();

    printf("Voytalk Test: %s %s\r\n", __DATE__, __TIME__);
}

/*****************************************************************************/
/* Compatibility                                                             */
/*****************************************************************************/

#if defined(YOTTA_MINAR_VERSION_STRING)
/*********************************************************/
/* Build for mbed OS                                     */
/*********************************************************/

void signalReady()
{
    minar::Scheduler::postCallback(blockServerSendNotification);
}

#else
/*********************************************************/
/* Build for mbed Classic                                */
/*********************************************************/

bool sendNotification = false;

void signalReady()
{
    sendNotification = true;
}

int main(void)
{
    app_start(0, NULL);

    for(;;)
    {
        // send notification outside of interrupt context
        if (sendNotification)
        {
            sendNotification = false;
            blockServerSendNotification();
        }

        ble.waitForEvent();
    }
}
#endif

<|endoftext|>


<commit_before>#include "http/json.hpp"

#include <stdlib.h>

#include <set>
#include <vector>

#include "http/http.hpp"
#include "stl_utils.hpp"
#include "utils.hpp"
#include "containers/archive/stl_types.hpp"

#ifndef NDEBUG
std::string (*cJSON_default_print)(cJSON *json) = cJSON_print_std_string;
#else
std::string (*cJSON_default_print)(cJSON *json) = cJSON_print_unformatted_std_string;
#endif
http_res_t http_json_res(cJSON *json) {
    return http_res_t(HTTP_OK, "application/json", cJSON_default_print(json));
}

cJSON *cJSON_merge(cJSON *lhs, cJSON *rhs) {
    rassert(lhs->type == cJSON_Object);
    rassert(rhs->type == cJSON_Object);
    cJSON *obj = cJSON_DeepCopy(lhs);

    for (int i = 0; i < cJSON_GetArraySize(rhs); ++i) {
        cJSON *item = cJSON_GetArrayItem(rhs, i);
        cJSON_DeleteItemFromObject(obj, item->string);
        cJSON_AddItemToObject(obj, item->string, cJSON_DeepCopy(item));
    }
    return obj;
}

scoped_cJSON_t::scoped_cJSON_t(cJSON *_val)
    : val(_val)
{ }

scoped_cJSON_t::~scoped_cJSON_t() {
    if (val) {
        cJSON_Delete(val);
    }
}

/* Render a cJSON entity to text for transfer/storage. */
std::string scoped_cJSON_t::Print() const THROWS_NOTHING {
    char *s = cJSON_Print(val);
    rassert(s);
    std::string res(s);
    free(s);

    return res;
}
/* Render a cJSON entity to text for transfer/storage without any formatting. */
std::string scoped_cJSON_t::PrintUnformatted() const THROWS_NOTHING {
    char *s = cJSON_PrintUnformatted(val);
    rassert(s);
    std::string res(s);
    free(s);

    return res;
}

std::string scoped_cJSON_t::PrintLexicographic() const THROWS_NOTHING {
    std::string acc;
    rassert(type() == cJSON_Number || type() == cJSON_String);
    if (type() == cJSON_Number) {
        acc += "N";

        union {double d; int64_t u;} packed;
        rassert(sizeof(packed.d) == sizeof(packed.u));
        rassert((void *)&packed.d == (void *)&packed.u);
        packed.d = val->valuedouble;
        acc += strprintf("%0.*lx", (int)(sizeof(double)*2), packed.u);
        return acc;
    } else {
        rassert(type() == cJSON_String);
        acc += "S";
        acc += val->valuestring;
    }
    return acc;
}

cJSON *scoped_cJSON_t::get() const {
    return val;
}

cJSON *scoped_cJSON_t::release() {
    cJSON *tmp = val;
    val = NULL;
    return tmp;
}

void scoped_cJSON_t::reset(cJSON *v) {
    if (val) {
        cJSON_Delete(val);
    }
    val = v;
}

copyable_cJSON_t::copyable_cJSON_t(cJSON *_val)
    : val(_val)
{ }

copyable_cJSON_t::copyable_cJSON_t(const copyable_cJSON_t &other)
    : val(cJSON_DeepCopy(other.val))
{ }

copyable_cJSON_t::~copyable_cJSON_t() {
    cJSON_Delete(val);
}

cJSON *copyable_cJSON_t::get() const {
    return val;
}

json_iterator_t::json_iterator_t(cJSON *target) {
    node = target->child;
}

cJSON *json_iterator_t::next() {
    cJSON *res = node;
    if (node) {
        node = node->next;
    }
    return res;
}

json_object_iterator_t::json_object_iterator_t(cJSON *target)
    : json_iterator_t(target)
{
    rassert(target);
    rassert(target->type == cJSON_Object);
}

json_array_iterator_t::json_array_iterator_t(cJSON *target)
    : json_iterator_t(target)
{
    rassert(target);
    rassert(target->type == cJSON_Array);
}

std::string cJSON_print_std_string(cJSON *json) THROWS_NOTHING {
    rassert(json);
    char *s = cJSON_Print(json);
    rassert(s);
    std::string res(s);
    free(s);

    return res;
}

std::string cJSON_print_unformatted_std_string(cJSON *json) THROWS_NOTHING {
    rassert(json);
    char *s = cJSON_PrintUnformatted(json);
    rassert(s);
    std::string res(s);
    free(s);

    return res;
}

void project(cJSON *json, std::set<std::string> keys) {
    rassert(json);
    rassert(json->type == cJSON_Object);

    json_object_iterator_t it(json);

    std::vector<std::string> keys_to_delete;

    while (cJSON *node = it.next()) {
        rassert(node->string);
        std::string str(node->string);
        if (!std_contains(keys, str)) {
            keys_to_delete.push_back(str);
        }
    }

    for (std::vector<std::string>::iterator jt = keys_to_delete.begin(); jt != keys_to_delete.end(); ++jt) {
        cJSON_DeleteItemFromObject(json, jt->c_str());
    }
}

cJSON *merge(cJSON *x, cJSON *y) {
    cJSON *res = cJSON_CreateObject();
    json_object_iterator_t xit(x), yit(y);

    std::set<std::string> keys;
    cJSON *hd;

    while ((hd = xit.next())) {
        rassert(hd->string);
        keys.insert(hd->string);
    }

    for (std::set<std::string>::iterator it = keys.begin();
                                         it != keys.end();
                                         ++it) {
        cJSON_AddItemToObject(res, it->c_str(), cJSON_DetachItemFromObject(x, it->c_str()));
    }

    keys.clear();

    while ((hd = yit.next())) {
        rassert(hd->string);
        keys.insert(hd->string);
    }

    for (std::set<std::string>::iterator it = keys.begin();
                                         it != keys.end();
                                         ++it) {
        rassert(!cJSON_GetObjectItem(res, it->c_str()), "Overlapping names in merge, name was: %s\n", it->c_str());
        cJSON_AddItemToObject(res, it->c_str(), cJSON_DetachItemFromObject(y, it->c_str()));
    }

    return res;
}

std::string cJSON_type_to_string(int type) {
    switch (type) {
    case cJSON_False: return "bool"; break;
    case cJSON_True: return "bool"; break;
    case cJSON_NULL: return "null"; break;
    case cJSON_Number: return "number"; break;
    case cJSON_String: return "string"; break;
    case cJSON_Array: return "array"; break;
    case cJSON_Object: return "object"; break;
    default: unreachable(); break;
    }
}

write_message_t &operator<<(write_message_t &msg, const cJSON &cjson) {
    msg << cjson.type;

    switch(cjson.type) {
    case cJSON_False:
        break;
    case cJSON_True:
        break;
    case cJSON_NULL:
        break;
    case cJSON_Number:
        msg << cjson.valuedouble;
        break;
    case cJSON_String:
        {
            rassert(cjson.valuestring);
            std::string s(cjson.valuestring);
            msg << s;
        }
        break;
    case cJSON_Array:
    case cJSON_Object:
        {
            msg << cJSON_GetArraySize(&cjson);

            cJSON *hd = cjson.child;
            while (hd) {
                if (cjson.type == cJSON_Object) {
                    rassert(hd->string);
                    msg << std::string(hd->string);
                }
                msg << *hd;
                hd = hd->next;
            }
        }
        break;
    default:
        crash("Unreachable");
        break;
    }
    return msg;
}

MUST_USE archive_result_t deserialize(read_stream_t *s, cJSON *cjson) {
    archive_result_t res = deserialize(s, &cjson->type);
    if (res) { return res; }

    switch (cjson->type) {
    case cJSON_False:
    case cJSON_True:
    case cJSON_NULL:
        return ARCHIVE_SUCCESS;
        break;
    case cJSON_Number:
        res = deserialize(s, &cjson->valuedouble);
        if (res) { return res; }
        cjson->valueint = static_cast<int>(cjson->valuedouble);
        return ARCHIVE_SUCCESS;
        break;
    case cJSON_String:
        {
            std::string str;
            res = deserialize(s, &str);
            if (res) { return res; }
            cjson->valuestring = strdup(str.c_str());
            return ARCHIVE_SUCCESS;
        }
        break;
    case cJSON_Array:
        {
            int size;
            res = deserialize(s, &size);
            if (res) { return res; }
            for (int i = 0; i < size; ++i) {
                cJSON *item = cJSON_CreateBlank();
                res = deserialize(s, item);
                if (res) { return res; }
                cJSON_AddItemToArray(cjson, item);
            }
            return ARCHIVE_SUCCESS;
        }
        break;
    case cJSON_Object:
        {
            int size;
            res = deserialize(s, &size);
            if (res) { return res; }
            for (int i = 0; i < size; ++i) {
                //grab the key
                std::string key;
                res = deserialize(s, &key);
                if (res) { return res; }

                //grab the item
                cJSON *item = cJSON_CreateBlank();
                res = deserialize(s, item);
                if (res) { return res; }
                cJSON_AddItemToObject(cjson, key.c_str(), item);
            }
            return ARCHIVE_SUCCESS;
        }
        break;
    default:
        crash("Unreachable");
        break;
    }
}
<commit_msg>fixing build error<commit_after>#include "http/json.hpp"

#include <stdlib.h>

#include <set>
#include <vector>

#include "http/http.hpp"
#include "stl_utils.hpp"
#include "utils.hpp"
#include "containers/archive/stl_types.hpp"

#ifndef NDEBUG
std::string (*cJSON_default_print)(cJSON *json) = cJSON_print_std_string;
#else
std::string (*cJSON_default_print)(cJSON *json) = cJSON_print_unformatted_std_string;
#endif
http_res_t http_json_res(cJSON *json) {
    return http_res_t(HTTP_OK, "application/json", cJSON_default_print(json));
}

cJSON *cJSON_merge(cJSON *lhs, cJSON *rhs) {
    rassert(lhs->type == cJSON_Object);
    rassert(rhs->type == cJSON_Object);
    cJSON *obj = cJSON_DeepCopy(lhs);

    for (int i = 0; i < cJSON_GetArraySize(rhs); ++i) {
        cJSON *item = cJSON_GetArrayItem(rhs, i);
        cJSON_DeleteItemFromObject(obj, item->string);
        cJSON_AddItemToObject(obj, item->string, cJSON_DeepCopy(item));
    }
    return obj;
}

scoped_cJSON_t::scoped_cJSON_t(cJSON *_val)
    : val(_val)
{ }

scoped_cJSON_t::~scoped_cJSON_t() {
    if (val) {
        cJSON_Delete(val);
    }
}

/* Render a cJSON entity to text for transfer/storage. */
std::string scoped_cJSON_t::Print() const THROWS_NOTHING {
    char *s = cJSON_Print(val);
    rassert(s);
    std::string res(s);
    free(s);

    return res;
}
/* Render a cJSON entity to text for transfer/storage without any formatting. */
std::string scoped_cJSON_t::PrintUnformatted() const THROWS_NOTHING {
    char *s = cJSON_PrintUnformatted(val);
    rassert(s);
    std::string res(s);
    free(s);

    return res;
}

std::string scoped_cJSON_t::PrintLexicographic() const THROWS_NOTHING {
    std::string acc;
    rassert(type() == cJSON_Number || type() == cJSON_String);
    if (type() == cJSON_Number) {
        acc += "N";

        union {double d; int64_t u;} packed;
        rassert(sizeof(packed.d) == sizeof(packed.u));
        rassert((void *)&packed.d == (void *)&packed.u);
        packed.d = val->valuedouble;
        acc += strprintf("%.*lx", (int)(sizeof(double)*2), packed.u);
        return acc;
    } else {
        rassert(type() == cJSON_String);
        acc += "S";
        acc += val->valuestring;
    }
    return acc;
}

cJSON *scoped_cJSON_t::get() const {
    return val;
}

cJSON *scoped_cJSON_t::release() {
    cJSON *tmp = val;
    val = NULL;
    return tmp;
}

void scoped_cJSON_t::reset(cJSON *v) {
    if (val) {
        cJSON_Delete(val);
    }
    val = v;
}

copyable_cJSON_t::copyable_cJSON_t(cJSON *_val)
    : val(_val)
{ }

copyable_cJSON_t::copyable_cJSON_t(const copyable_cJSON_t &other)
    : val(cJSON_DeepCopy(other.val))
{ }

copyable_cJSON_t::~copyable_cJSON_t() {
    cJSON_Delete(val);
}

cJSON *copyable_cJSON_t::get() const {
    return val;
}

json_iterator_t::json_iterator_t(cJSON *target) {
    node = target->child;
}

cJSON *json_iterator_t::next() {
    cJSON *res = node;
    if (node) {
        node = node->next;
    }
    return res;
}

json_object_iterator_t::json_object_iterator_t(cJSON *target)
    : json_iterator_t(target)
{
    rassert(target);
    rassert(target->type == cJSON_Object);
}

json_array_iterator_t::json_array_iterator_t(cJSON *target)
    : json_iterator_t(target)
{
    rassert(target);
    rassert(target->type == cJSON_Array);
}

std::string cJSON_print_std_string(cJSON *json) THROWS_NOTHING {
    rassert(json);
    char *s = cJSON_Print(json);
    rassert(s);
    std::string res(s);
    free(s);

    return res;
}

std::string cJSON_print_unformatted_std_string(cJSON *json) THROWS_NOTHING {
    rassert(json);
    char *s = cJSON_PrintUnformatted(json);
    rassert(s);
    std::string res(s);
    free(s);

    return res;
}

void project(cJSON *json, std::set<std::string> keys) {
    rassert(json);
    rassert(json->type == cJSON_Object);

    json_object_iterator_t it(json);

    std::vector<std::string> keys_to_delete;

    while (cJSON *node = it.next()) {
        rassert(node->string);
        std::string str(node->string);
        if (!std_contains(keys, str)) {
            keys_to_delete.push_back(str);
        }
    }

    for (std::vector<std::string>::iterator jt = keys_to_delete.begin(); jt != keys_to_delete.end(); ++jt) {
        cJSON_DeleteItemFromObject(json, jt->c_str());
    }
}

cJSON *merge(cJSON *x, cJSON *y) {
    cJSON *res = cJSON_CreateObject();
    json_object_iterator_t xit(x), yit(y);

    std::set<std::string> keys;
    cJSON *hd;

    while ((hd = xit.next())) {
        rassert(hd->string);
        keys.insert(hd->string);
    }

    for (std::set<std::string>::iterator it = keys.begin();
                                         it != keys.end();
                                         ++it) {
        cJSON_AddItemToObject(res, it->c_str(), cJSON_DetachItemFromObject(x, it->c_str()));
    }

    keys.clear();

    while ((hd = yit.next())) {
        rassert(hd->string);
        keys.insert(hd->string);
    }

    for (std::set<std::string>::iterator it = keys.begin();
                                         it != keys.end();
                                         ++it) {
        rassert(!cJSON_GetObjectItem(res, it->c_str()), "Overlapping names in merge, name was: %s\n", it->c_str());
        cJSON_AddItemToObject(res, it->c_str(), cJSON_DetachItemFromObject(y, it->c_str()));
    }

    return res;
}

std::string cJSON_type_to_string(int type) {
    switch (type) {
    case cJSON_False: return "bool"; break;
    case cJSON_True: return "bool"; break;
    case cJSON_NULL: return "null"; break;
    case cJSON_Number: return "number"; break;
    case cJSON_String: return "string"; break;
    case cJSON_Array: return "array"; break;
    case cJSON_Object: return "object"; break;
    default: unreachable(); break;
    }
}

write_message_t &operator<<(write_message_t &msg, const cJSON &cjson) {
    msg << cjson.type;

    switch(cjson.type) {
    case cJSON_False:
        break;
    case cJSON_True:
        break;
    case cJSON_NULL:
        break;
    case cJSON_Number:
        msg << cjson.valuedouble;
        break;
    case cJSON_String:
        {
            rassert(cjson.valuestring);
            std::string s(cjson.valuestring);
            msg << s;
        }
        break;
    case cJSON_Array:
    case cJSON_Object:
        {
            msg << cJSON_GetArraySize(&cjson);

            cJSON *hd = cjson.child;
            while (hd) {
                if (cjson.type == cJSON_Object) {
                    rassert(hd->string);
                    msg << std::string(hd->string);
                }
                msg << *hd;
                hd = hd->next;
            }
        }
        break;
    default:
        crash("Unreachable");
        break;
    }
    return msg;
}

MUST_USE archive_result_t deserialize(read_stream_t *s, cJSON *cjson) {
    archive_result_t res = deserialize(s, &cjson->type);
    if (res) { return res; }

    switch (cjson->type) {
    case cJSON_False:
    case cJSON_True:
    case cJSON_NULL:
        return ARCHIVE_SUCCESS;
        break;
    case cJSON_Number:
        res = deserialize(s, &cjson->valuedouble);
        if (res) { return res; }
        cjson->valueint = static_cast<int>(cjson->valuedouble);
        return ARCHIVE_SUCCESS;
        break;
    case cJSON_String:
        {
            std::string str;
            res = deserialize(s, &str);
            if (res) { return res; }
            cjson->valuestring = strdup(str.c_str());
            return ARCHIVE_SUCCESS;
        }
        break;
    case cJSON_Array:
        {
            int size;
            res = deserialize(s, &size);
            if (res) { return res; }
            for (int i = 0; i < size; ++i) {
                cJSON *item = cJSON_CreateBlank();
                res = deserialize(s, item);
                if (res) { return res; }
                cJSON_AddItemToArray(cjson, item);
            }
            return ARCHIVE_SUCCESS;
        }
        break;
    case cJSON_Object:
        {
            int size;
            res = deserialize(s, &size);
            if (res) { return res; }
            for (int i = 0; i < size; ++i) {
                //grab the key
                std::string key;
                res = deserialize(s, &key);
                if (res) { return res; }

                //grab the item
                cJSON *item = cJSON_CreateBlank();
                res = deserialize(s, item);
                if (res) { return res; }
                cJSON_AddItemToObject(cjson, key.c_str(), item);
            }
            return ARCHIVE_SUCCESS;
        }
        break;
    default:
        crash("Unreachable");
        break;
    }
}
<|endoftext|>


<commit_before>/*
* Copyright 2019 Google LLC
*
* Use of this source code is governed by a BSD-style license that can be
* found in the LICENSE file.
*/

#include "tools/viewer/SkSLSlide.h"

#include "include/core/SkCanvas.h"
#include "include/core/SkFont.h"
#include "include/effects/SkGradientShader.h"
#include "include/effects/SkPerlinNoiseShader.h"
#include "include/sksl/SkSLDebugTrace.h"
#include "src/core/SkEnumerate.h"
#include "tools/Resources.h"
#include "tools/viewer/Viewer.h"

#include <algorithm>
#include <cstdio>
#include "imgui.h"

using namespace sk_app;

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

static int InputTextCallback(ImGuiInputTextCallbackData* data) {
    if (data->EventFlag == ImGuiInputTextFlags_CallbackResize) {
        SkString* s = (SkString*)data->UserData;
        SkASSERT(data->Buf == s->writable_str());
        SkString tmp(data->Buf, data->BufTextLen);
        s->swap(tmp);
        data->Buf = s->writable_str();
    }
    return 0;
}

SkSLSlide::SkSLSlide() {
    // Register types for serialization
    fName = "SkSL";

    fSkSL =

        "uniform shader child;\n"
        "\n"
        "half4 main(float2 p) {\n"
        "    return child.eval(p);\n"
        "}\n";

    fCodeIsDirty = true;
}

void SkSLSlide::load(SkScalar winWidth, SkScalar winHeight) {
    SkPoint points[] = { { 0, 0 }, { 256, 0 } };
    SkColor colors[] = { SK_ColorRED, SK_ColorGREEN };

    sk_sp<SkShader> shader;

    fShaders.push_back(std::make_pair("Null", nullptr));

    shader = SkGradientShader::MakeLinear(points, colors, nullptr, 2, SkTileMode::kClamp);
    fShaders.push_back(std::make_pair("Linear Gradient", shader));

    shader = SkGradientShader::MakeRadial({ 256, 256 }, 256, colors, nullptr, 2,
                                          SkTileMode::kClamp);
    fShaders.push_back(std::make_pair("Radial Gradient", shader));

    shader = SkGradientShader::MakeSweep(256, 256, colors, nullptr, 2);
    fShaders.push_back(std::make_pair("Sweep Gradient", shader));

    shader = GetResourceAsImage("images/mandrill_256.png")->makeShader(SkSamplingOptions());
    fShaders.push_back(std::make_pair("Mandrill", shader));

    fResolution = { winWidth, winHeight, 1.0f };
}

void SkSLSlide::unload() {
    fEffect.reset();
    fInputs.reset();
    fChildren.reset();
    fShaders.reset();
}

bool SkSLSlide::rebuild() {
    // Some of the standard shadertoy inputs:
    SkString sksl("uniform float3 iResolution;\n"
                  "uniform float  iTime;\n"
                  "uniform float4 iMouse;\n");
    sksl.append(fSkSL);

    // It shouldn't happen, but it's possible to assert in the compiler, especially mid-edit.
    // To guard against losing your work, write out the shader to a backup file, then remove it
    // when we compile successfully.
    constexpr char kBackupFile[] = "sksl.bak";
    FILE* backup = fopen(kBackupFile, "w");
    if (backup) {
        fwrite(fSkSL.c_str(), 1, fSkSL.size(), backup);
        fclose(backup);
    }
    auto [effect, errorText] = SkRuntimeEffect::MakeForShader(sksl);
    if (backup) {
        std::remove(kBackupFile);
    }

    if (!effect) {
        Viewer::ShaderErrorHandler()->compileError(sksl.c_str(), errorText.c_str());
        return false;
    }

    size_t oldSize = fEffect ? fEffect->uniformSize() : 0;
    fInputs.realloc(effect->uniformSize());
    if (effect->uniformSize() > oldSize) {
        memset(fInputs.get() + oldSize, 0, effect->uniformSize() - oldSize);
    }
    fChildren.resize_back(effect->children().size());

    fEffect = effect;
    fCodeIsDirty = false;
    return true;
}

void SkSLSlide::draw(SkCanvas* canvas) {
    canvas->clear(SK_ColorWHITE);

    ImGui::Begin("SkSL", nullptr, ImGuiWindowFlags_AlwaysVerticalScrollbar);

    // Edit box for shader code
    ImGuiInputTextFlags flags = ImGuiInputTextFlags_CallbackResize;
    ImVec2 boxSize(-1.0f, ImGui::GetTextLineHeight() * 30);
    if (ImGui::InputTextMultiline("Code", fSkSL.writable_str(), fSkSL.size() + 1, boxSize, flags,
                                  InputTextCallback, &fSkSL)) {
        fCodeIsDirty = true;
    }

    if (fCodeIsDirty || !fEffect) {
        this->rebuild();
    }

    if (!fEffect) {
        ImGui::End();
        return;
    }

    bool writeTrace = false;
    bool writeDump = false;
    if (!canvas->recordingContext()) {
        ImGui::InputInt2("Trace Coordinate (X/Y)", fTraceCoord);
        writeTrace = ImGui::Button("Write Debug Trace (JSON)");
        writeDump = ImGui::Button("Write Debug Dump (Human-Readable)");
    }

    // Update fMousePos
    ImVec2 mousePos = ImGui::GetMousePos();
    if (ImGui::IsMouseDown(0)) {
        fMousePos.x = mousePos.x;
        fMousePos.y = mousePos.y;
    }
    if (ImGui::IsMouseClicked(0)) {
        fMousePos.z = mousePos.x;
        fMousePos.w = mousePos.y;
    }
    fMousePos.z = abs(fMousePos.z) * (ImGui::IsMouseDown(0)    ? 1 : -1);
    fMousePos.w = abs(fMousePos.w) * (ImGui::IsMouseClicked(0) ? 1 : -1);

    for (const auto& v : fEffect->uniforms()) {
        char* data = fInputs.get() + v.offset;
        if (v.name.equals("iResolution")) {
            memcpy(data, &fResolution, sizeof(fResolution));
            continue;
        }
        if (v.name.equals("iTime")) {
            memcpy(data, &fSeconds, sizeof(fSeconds));
            continue;
        }
        if (v.name.equals("iMouse")) {
            memcpy(data, &fMousePos, sizeof(fMousePos));
            continue;
        }
        switch (v.type) {
            case SkRuntimeEffect::Uniform::Type::kFloat:
            case SkRuntimeEffect::Uniform::Type::kFloat2:
            case SkRuntimeEffect::Uniform::Type::kFloat3:
            case SkRuntimeEffect::Uniform::Type::kFloat4: {
                int rows = ((int)v.type - (int)SkRuntimeEffect::Uniform::Type::kFloat) + 1;
                float* f = reinterpret_cast<float*>(data);
                for (int c = 0; c < v.count; ++c, f += rows) {
                    SkString name = v.isArray() ? SkStringPrintf("%s[%d]", v.name.c_str(), c)
                                                : v.name;
                    ImGui::PushID(c);
                    ImGui::DragScalarN(name.c_str(), ImGuiDataType_Float, f, rows, 1.0f);
                    ImGui::PopID();
                }
                break;
            }
            case SkRuntimeEffect::Uniform::Type::kFloat2x2:
            case SkRuntimeEffect::Uniform::Type::kFloat3x3:
            case SkRuntimeEffect::Uniform::Type::kFloat4x4: {
                int rows = ((int)v.type - (int)SkRuntimeEffect::Uniform::Type::kFloat2x2) + 2;
                int cols = rows;
                float* f = reinterpret_cast<float*>(data);
                for (int e = 0; e < v.count; ++e) {
                    for (int c = 0; c < cols; ++c, f += rows) {
                        SkString name = v.isArray()
                            ? SkStringPrintf("%s[%d][%d]", v.name.c_str(), e, c)
                            : SkStringPrintf("%s[%d]", v.name.c_str(), c);
                        ImGui::DragScalarN(name.c_str(), ImGuiDataType_Float, f, rows, 1.0f);
                    }
                }
                break;
            }
            case SkRuntimeEffect::Uniform::Type::kInt:
            case SkRuntimeEffect::Uniform::Type::kInt2:
            case SkRuntimeEffect::Uniform::Type::kInt3:
            case SkRuntimeEffect::Uniform::Type::kInt4: {
                int rows = ((int)v.type - (int)SkRuntimeEffect::Uniform::Type::kInt) + 1;
                int* i = reinterpret_cast<int*>(data);
                for (int c = 0; c < v.count; ++c, i += rows) {
                    SkString name = v.isArray() ? SkStringPrintf("%s[%d]", v.name.c_str(), c)
                                                : v.name;
                    ImGui::PushID(c);
                    ImGui::DragScalarN(name.c_str(), ImGuiDataType_S32, i, rows, 1.0f);
                    ImGui::PopID();
                }
                break;
            }
        }
    }

    for (const auto& c : fEffect->children()) {
        auto curShader =
                std::find_if(fShaders.begin(), fShaders.end(), [tgt = fChildren[c.index]](auto p) {
                    return p.second == tgt;
                });
        SkASSERT(curShader != fShaders.end());

        if (ImGui::BeginCombo(c.name.c_str(), curShader->first)) {
            for (const auto& namedShader : fShaders) {
                if (ImGui::Selectable(namedShader.first, curShader->second == namedShader.second)) {
                    fChildren[c.index] = namedShader.second;
                }
            }
            ImGui::EndCombo();
        }
    }

    static SkColor4f gPaintColor { 1.0f, 1.0f, 1.0f , 1.0f };
    ImGui::ColorEdit4("Paint Color", gPaintColor.vec());

    ImGui::RadioButton("Fill",      &fGeometry, kFill);      ImGui::SameLine();
    ImGui::RadioButton("Circle",    &fGeometry, kCircle);    ImGui::SameLine();
    ImGui::RadioButton("RoundRect", &fGeometry, kRoundRect); ImGui::SameLine();
    ImGui::RadioButton("Capsule",   &fGeometry, kCapsule);   ImGui::SameLine();
    ImGui::RadioButton("Text",      &fGeometry, kText);

    ImGui::End();

    auto inputs = SkData::MakeWithoutCopy(fInputs.get(), fEffect->uniformSize());

    sk_sp<SkSL::DebugTrace> debugTrace;
    auto shader = fEffect->makeShader(std::move(inputs), fChildren.data(), fChildren.count(),
                                      nullptr, false);
    if (writeTrace || writeDump) {
        SkIPoint traceCoord = {fTraceCoord[0], fTraceCoord[1]};
        SkRuntimeEffect::TracedShader traced = SkRuntimeEffect::MakeTraced(std::move(shader),
                                                                           traceCoord);
        shader = std::move(traced.shader);
        debugTrace = std::move(traced.debugTrace);
    }
    SkPaint p;
    p.setColor4f(gPaintColor);
    p.setShader(std::move(shader));

    switch (fGeometry) {
        case kFill:
            canvas->drawPaint(p);
            break;
        case kCircle:
            canvas->drawCircle({ 256, 256 }, 256, p);
            break;
        case kRoundRect:
            canvas->drawRoundRect({ 0, 0, 512, 512 }, 64, 64, p);
            break;
        case kCapsule:
            canvas->drawRoundRect({ 0, 224, 512, 288 }, 32, 32, p);
            break;
        case kText: {
            SkFont font;
            font.setSize(SkIntToScalar(96));
            canvas->drawSimpleText("Hello World", strlen("Hello World"), SkTextEncoding::kUTF8, 0,
                                   256, font, p);
        } break;
        default: break;
    }

    if (debugTrace && writeTrace) {
        SkFILEWStream traceFile("SkVMDebugTrace.json");
        debugTrace->writeTrace(&traceFile);
    }
    if (debugTrace && writeDump) {
        SkFILEWStream dumpFile("SkVMDebugTrace.dump.txt");
        debugTrace->dump(&dumpFile);
    }
}

bool SkSLSlide::animate(double nanos) {
    fSeconds = static_cast<float>(nanos * 1E-9);
    return true;
}
<commit_msg>Improve debug trace generation speed by clipping the paint.<commit_after>/*
* Copyright 2019 Google LLC
*
* Use of this source code is governed by a BSD-style license that can be
* found in the LICENSE file.
*/

#include "tools/viewer/SkSLSlide.h"

#include "include/core/SkCanvas.h"
#include "include/core/SkFont.h"
#include "include/effects/SkGradientShader.h"
#include "include/effects/SkPerlinNoiseShader.h"
#include "include/sksl/SkSLDebugTrace.h"
#include "src/core/SkEnumerate.h"
#include "tools/Resources.h"
#include "tools/viewer/Viewer.h"

#include <algorithm>
#include <cstdio>
#include "imgui.h"

using namespace sk_app;

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

static int InputTextCallback(ImGuiInputTextCallbackData* data) {
    if (data->EventFlag == ImGuiInputTextFlags_CallbackResize) {
        SkString* s = (SkString*)data->UserData;
        SkASSERT(data->Buf == s->writable_str());
        SkString tmp(data->Buf, data->BufTextLen);
        s->swap(tmp);
        data->Buf = s->writable_str();
    }
    return 0;
}

SkSLSlide::SkSLSlide() {
    // Register types for serialization
    fName = "SkSL";

    fSkSL =

        "uniform shader child;\n"
        "\n"
        "half4 main(float2 p) {\n"
        "    return child.eval(p);\n"
        "}\n";

    fCodeIsDirty = true;
}

void SkSLSlide::load(SkScalar winWidth, SkScalar winHeight) {
    SkPoint points[] = { { 0, 0 }, { 256, 0 } };
    SkColor colors[] = { SK_ColorRED, SK_ColorGREEN };

    sk_sp<SkShader> shader;

    fShaders.push_back(std::make_pair("Null", nullptr));

    shader = SkGradientShader::MakeLinear(points, colors, nullptr, 2, SkTileMode::kClamp);
    fShaders.push_back(std::make_pair("Linear Gradient", shader));

    shader = SkGradientShader::MakeRadial({ 256, 256 }, 256, colors, nullptr, 2,
                                          SkTileMode::kClamp);
    fShaders.push_back(std::make_pair("Radial Gradient", shader));

    shader = SkGradientShader::MakeSweep(256, 256, colors, nullptr, 2);
    fShaders.push_back(std::make_pair("Sweep Gradient", shader));

    shader = GetResourceAsImage("images/mandrill_256.png")->makeShader(SkSamplingOptions());
    fShaders.push_back(std::make_pair("Mandrill", shader));

    fResolution = { winWidth, winHeight, 1.0f };
}

void SkSLSlide::unload() {
    fEffect.reset();
    fInputs.reset();
    fChildren.reset();
    fShaders.reset();
}

bool SkSLSlide::rebuild() {
    // Some of the standard shadertoy inputs:
    SkString sksl("uniform float3 iResolution;\n"
                  "uniform float  iTime;\n"
                  "uniform float4 iMouse;\n");
    sksl.append(fSkSL);

    // It shouldn't happen, but it's possible to assert in the compiler, especially mid-edit.
    // To guard against losing your work, write out the shader to a backup file, then remove it
    // when we compile successfully.
    constexpr char kBackupFile[] = "sksl.bak";
    FILE* backup = fopen(kBackupFile, "w");
    if (backup) {
        fwrite(fSkSL.c_str(), 1, fSkSL.size(), backup);
        fclose(backup);
    }
    auto [effect, errorText] = SkRuntimeEffect::MakeForShader(sksl);
    if (backup) {
        std::remove(kBackupFile);
    }

    if (!effect) {
        Viewer::ShaderErrorHandler()->compileError(sksl.c_str(), errorText.c_str());
        return false;
    }

    size_t oldSize = fEffect ? fEffect->uniformSize() : 0;
    fInputs.realloc(effect->uniformSize());
    if (effect->uniformSize() > oldSize) {
        memset(fInputs.get() + oldSize, 0, effect->uniformSize() - oldSize);
    }
    fChildren.resize_back(effect->children().size());

    fEffect = effect;
    fCodeIsDirty = false;
    return true;
}

void SkSLSlide::draw(SkCanvas* canvas) {
    canvas->clear(SK_ColorWHITE);

    ImGui::Begin("SkSL", nullptr, ImGuiWindowFlags_AlwaysVerticalScrollbar);

    // Edit box for shader code
    ImGuiInputTextFlags flags = ImGuiInputTextFlags_CallbackResize;
    ImVec2 boxSize(-1.0f, ImGui::GetTextLineHeight() * 30);
    if (ImGui::InputTextMultiline("Code", fSkSL.writable_str(), fSkSL.size() + 1, boxSize, flags,
                                  InputTextCallback, &fSkSL)) {
        fCodeIsDirty = true;
    }

    if (fCodeIsDirty || !fEffect) {
        this->rebuild();
    }

    if (!fEffect) {
        ImGui::End();
        return;
    }

    bool writeTrace = false;
    bool writeDump = false;
    if (!canvas->recordingContext()) {
        ImGui::InputInt2("Trace Coordinate (X/Y)", fTraceCoord);
        writeTrace = ImGui::Button("Write Debug Trace (JSON)");
        writeDump = ImGui::Button("Write Debug Dump (Human-Readable)");
    }

    // Update fMousePos
    ImVec2 mousePos = ImGui::GetMousePos();
    if (ImGui::IsMouseDown(0)) {
        fMousePos.x = mousePos.x;
        fMousePos.y = mousePos.y;
    }
    if (ImGui::IsMouseClicked(0)) {
        fMousePos.z = mousePos.x;
        fMousePos.w = mousePos.y;
    }
    fMousePos.z = abs(fMousePos.z) * (ImGui::IsMouseDown(0)    ? 1 : -1);
    fMousePos.w = abs(fMousePos.w) * (ImGui::IsMouseClicked(0) ? 1 : -1);

    for (const auto& v : fEffect->uniforms()) {
        char* data = fInputs.get() + v.offset;
        if (v.name.equals("iResolution")) {
            memcpy(data, &fResolution, sizeof(fResolution));
            continue;
        }
        if (v.name.equals("iTime")) {
            memcpy(data, &fSeconds, sizeof(fSeconds));
            continue;
        }
        if (v.name.equals("iMouse")) {
            memcpy(data, &fMousePos, sizeof(fMousePos));
            continue;
        }
        switch (v.type) {
            case SkRuntimeEffect::Uniform::Type::kFloat:
            case SkRuntimeEffect::Uniform::Type::kFloat2:
            case SkRuntimeEffect::Uniform::Type::kFloat3:
            case SkRuntimeEffect::Uniform::Type::kFloat4: {
                int rows = ((int)v.type - (int)SkRuntimeEffect::Uniform::Type::kFloat) + 1;
                float* f = reinterpret_cast<float*>(data);
                for (int c = 0; c < v.count; ++c, f += rows) {
                    SkString name = v.isArray() ? SkStringPrintf("%s[%d]", v.name.c_str(), c)
                                                : v.name;
                    ImGui::PushID(c);
                    ImGui::DragScalarN(name.c_str(), ImGuiDataType_Float, f, rows, 1.0f);
                    ImGui::PopID();
                }
                break;
            }
            case SkRuntimeEffect::Uniform::Type::kFloat2x2:
            case SkRuntimeEffect::Uniform::Type::kFloat3x3:
            case SkRuntimeEffect::Uniform::Type::kFloat4x4: {
                int rows = ((int)v.type - (int)SkRuntimeEffect::Uniform::Type::kFloat2x2) + 2;
                int cols = rows;
                float* f = reinterpret_cast<float*>(data);
                for (int e = 0; e < v.count; ++e) {
                    for (int c = 0; c < cols; ++c, f += rows) {
                        SkString name = v.isArray()
                            ? SkStringPrintf("%s[%d][%d]", v.name.c_str(), e, c)
                            : SkStringPrintf("%s[%d]", v.name.c_str(), c);
                        ImGui::DragScalarN(name.c_str(), ImGuiDataType_Float, f, rows, 1.0f);
                    }
                }
                break;
            }
            case SkRuntimeEffect::Uniform::Type::kInt:
            case SkRuntimeEffect::Uniform::Type::kInt2:
            case SkRuntimeEffect::Uniform::Type::kInt3:
            case SkRuntimeEffect::Uniform::Type::kInt4: {
                int rows = ((int)v.type - (int)SkRuntimeEffect::Uniform::Type::kInt) + 1;
                int* i = reinterpret_cast<int*>(data);
                for (int c = 0; c < v.count; ++c, i += rows) {
                    SkString name = v.isArray() ? SkStringPrintf("%s[%d]", v.name.c_str(), c)
                                                : v.name;
                    ImGui::PushID(c);
                    ImGui::DragScalarN(name.c_str(), ImGuiDataType_S32, i, rows, 1.0f);
                    ImGui::PopID();
                }
                break;
            }
        }
    }

    for (const auto& c : fEffect->children()) {
        auto curShader =
                std::find_if(fShaders.begin(), fShaders.end(), [tgt = fChildren[c.index]](auto p) {
                    return p.second == tgt;
                });
        SkASSERT(curShader != fShaders.end());

        if (ImGui::BeginCombo(c.name.c_str(), curShader->first)) {
            for (const auto& namedShader : fShaders) {
                if (ImGui::Selectable(namedShader.first, curShader->second == namedShader.second)) {
                    fChildren[c.index] = namedShader.second;
                }
            }
            ImGui::EndCombo();
        }
    }

    static SkColor4f gPaintColor { 1.0f, 1.0f, 1.0f , 1.0f };
    ImGui::ColorEdit4("Paint Color", gPaintColor.vec());

    ImGui::RadioButton("Fill",      &fGeometry, kFill);      ImGui::SameLine();
    ImGui::RadioButton("Circle",    &fGeometry, kCircle);    ImGui::SameLine();
    ImGui::RadioButton("RoundRect", &fGeometry, kRoundRect); ImGui::SameLine();
    ImGui::RadioButton("Capsule",   &fGeometry, kCapsule);   ImGui::SameLine();
    ImGui::RadioButton("Text",      &fGeometry, kText);

    ImGui::End();

    auto inputs = SkData::MakeWithoutCopy(fInputs.get(), fEffect->uniformSize());

    canvas->save();

    sk_sp<SkSL::DebugTrace> debugTrace;
    auto shader = fEffect->makeShader(std::move(inputs), fChildren.data(), fChildren.count(),
                                      nullptr, false);
    if (writeTrace || writeDump) {
        SkIPoint traceCoord = {fTraceCoord[0], fTraceCoord[1]};
        SkRuntimeEffect::TracedShader traced = SkRuntimeEffect::MakeTraced(std::move(shader),
                                                                           traceCoord);
        shader = std::move(traced.shader);
        debugTrace = std::move(traced.debugTrace);

        // Reduce debug trace delay by clipping to a 4x4 rectangle for this paint, centered on the
        // pixel to trace. A minor complication is that the canvas might have a transform applied to
        // it, but we want to clip in device space. This can be worked around by resetting the
        // canvas matrix temporarily.
        SkM44 canvasMatrix = canvas->getLocalToDevice();
        canvas->resetMatrix();
        auto r = SkRect::MakeXYWH(fTraceCoord[0] - 1, fTraceCoord[1] - 1, 4, 4);
        canvas->clipRect(r, SkClipOp::kIntersect);
        canvas->setMatrix(canvasMatrix);
    }
    SkPaint p;
    p.setColor4f(gPaintColor);
    p.setShader(std::move(shader));

    switch (fGeometry) {
        case kFill:
            canvas->drawPaint(p);
            break;
        case kCircle:
            canvas->drawCircle({ 256, 256 }, 256, p);
            break;
        case kRoundRect:
            canvas->drawRoundRect({ 0, 0, 512, 512 }, 64, 64, p);
            break;
        case kCapsule:
            canvas->drawRoundRect({ 0, 224, 512, 288 }, 32, 32, p);
            break;
        case kText: {
            SkFont font;
            font.setSize(SkIntToScalar(96));
            canvas->drawSimpleText("Hello World", strlen("Hello World"), SkTextEncoding::kUTF8, 0,
                                   256, font, p);
        } break;
        default: break;
    }

    canvas->restore();

    if (debugTrace && writeTrace) {
        SkFILEWStream traceFile("SkVMDebugTrace.json");
        debugTrace->writeTrace(&traceFile);
    }
    if (debugTrace && writeDump) {
        SkFILEWStream dumpFile("SkVMDebugTrace.dump.txt");
        debugTrace->dump(&dumpFile);
    }
}

bool SkSLSlide::animate(double nanos) {
    fSeconds = static_cast<float>(nanos * 1E-9);
    return true;
}
<|endoftext|>