<commit_before>/* See other files here for the LICENCE that applies here. */
/*
Template for new files, replace word "template" and later delete this line here.
*/

#ifndef INCLUDE_OT_NEWCLI_template
#define INCLUDE_OT_NEWCLI_template

#include "lib_common1.hpp"

namespace nOT {

INJECT_OT_COMMON_USING_NAMESPACE_COMMON_1; // <=== namespaces

/** Global options to run this program main() Eg used for developer's special options like +setdemo +setdebug.
This is NOT for all the other options that are parsed and executed by program. */
class cRunOptions {
	public:
		enum tRunMode { ///< Type of run mode - is this normal, or demonstration etc.
			eRunModeCurrent=1, ///< currently developed version
			eRunModeDemo, ///< best currently available Demo of something nice
			eRunModeNormal, ///< do the normal things that the program should do
		};

	private:
		tRunMode mRunMode; ///< selected run mode

		bool mDebug; // turn debug on, Eg: +debug without it probably nothing will be written to debug (maybe just error etc)
		bool mDebugSendToFile; // send to file, Eg: for +debugfile ; also turns on debug
		bool mDebugSendToCerr; // send to cerr, Eg: for +debugcerr ; also turns on debug
		// if debug is set but not any other DebugSend* then we will default to sending to debugcerr

		bool mDoRunDebugshow;

	public:
		const tRunMode getTRunMode() { return mRunMode; }
		const bool getDebug() { return mDebug; }
		const bool getDebugSendToFile() { return mDebugSendToFile; }
		const bool getDebugSendToCerr() { return mDebugSendToCerr; }
		const bool getDoRunDebugshow() { return mDoRunDebugshow; }

		cRunOptions();

		vector<string> ExecuteRunoptionsAndRemoveThem(const vector<string> & args);
		void Exec(const string & runoption); // eg: Exec("+debug");

		void Normalize();
};

extern cRunOptions gRunOptions;

}; // namespace nOT

#endif

<commit_msg>[detail] include guard name in runoptions<commit_after>/* See other files here for the LICENCE that applies here. */
/*
Template for new files, replace word "template" and later delete this line here.
*/

#ifndef INCLUDE_OT_NEWCLI_runoptions_hpp
#define INCLUDE_OT_NEWCLI_runoptions_hpp

#include "lib_common1.hpp"

namespace nOT {

INJECT_OT_COMMON_USING_NAMESPACE_COMMON_1; // <=== namespaces

/** Global options to run this program main() Eg used for developer's special options like +setdemo +setdebug.
This is NOT for all the other options that are parsed and executed by program. */
class cRunOptions {
	public:
		enum tRunMode { ///< Type of run mode - is this normal, or demonstration etc.
			eRunModeCurrent=1, ///< currently developed version
			eRunModeDemo, ///< best currently available Demo of something nice
			eRunModeNormal, ///< do the normal things that the program should do
		};

	private:
		tRunMode mRunMode; ///< selected run mode

		bool mDebug; // turn debug on, Eg: +debug without it probably nothing will be written to debug (maybe just error etc)
		bool mDebugSendToFile; // send to file, Eg: for +debugfile ; also turns on debug
		bool mDebugSendToCerr; // send to cerr, Eg: for +debugcerr ; also turns on debug
		// if debug is set but not any other DebugSend* then we will default to sending to debugcerr

		bool mDoRunDebugshow;

	public:
		const tRunMode getTRunMode() { return mRunMode; }
		const bool getDebug() { return mDebug; }
		const bool getDebugSendToFile() { return mDebugSendToFile; }
		const bool getDebugSendToCerr() { return mDebugSendToCerr; }
		const bool getDoRunDebugshow() { return mDoRunDebugshow; }

		cRunOptions();

		vector<string> ExecuteRunoptionsAndRemoveThem(const vector<string> & args);
		void Exec(const string & runoption); // eg: Exec("+debug");

		void Normalize();
};

extern cRunOptions gRunOptions;

}; // namespace nOT

#endif

<|endoftext|>


<commit_before>/* This file is part of the UniSet project
 * Copyright (c) 2009 Free Software Foundation, Inc.
 * Copyright (c) 2009 Ivan Donchevskiy
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 2 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
 */
// --------------------------------------------------------------------------
/*! \file
 *  \author Ivan Donchevskiy
 *  \date   $Date: 2009/07/15 15:55:00 $
 *  \version $Id: Jrn.h,v 1.0 2009/07/15 15:55:00 vpashka Exp $
 */
// --------------------------------------------------------------------------

#include "stdio.h"
#include "stdlib.h"
#include "string.h"
#include "Storages.h"
#include "UniXML.h"

void testTable1(void)
{
	char *chr=new char[20];
	char *val=new char[40];
	TableStorage *t;
	t = new TableStorage("table.test", 40, 1220, 0);
	int i;
	for(i=0;i<20;i++)
	{
		chr[0]=i;
		sprintf(val,"%d",i);
		t->AddRow(chr,val);
	}
	printf("elements with values=keys added:\n");
	for(i=0;i<40;i++)
	{
		chr[0]=i;
		if(t->FindKeyValue(chr,val)!=0) printf("%s, ",val);
	}
	printf("\n");
	for(i=9;i<15;i++)
	{
		chr[0]=i;
		t->DelRow(chr);
	}
	printf("elements with keys from 9 to 14 deleted\n");
	for(i=9;i<15;i++)
	{
		chr[0]=i;
		sprintf(val,"%d",i+40);
		t->AddRow(chr,val);
	}
	printf("elements with keys from 9 to 14 with values=key+40 added, all elements:\n");
	for(i=0;i<40;i++)
	{
		chr[0]=i;
		if(t->FindKeyValue(chr,val)!=0) printf("%s, ",val);
	}
	printf("\n");
}

void testTable2(void)
{
	char *val=new char[40];
	TableBlockStorage *t;
	t = new TableBlockStorage("big_file.test", 4, 40, 20000, 5,28,0,true);
	int i;
	for(i=1;i<20;i++)
	{
		if(t->FindKeyValue(&i,val)!=0) printf("%s, ",val);
	}
	printf("\ncurrent block = %d\n",t->GetCurBlock());
	for(i=1;i<11;i++)
	{
		sprintf(val,"%d",i);
		t->AddRow((char*)&i,val);
	}
	printf("current block = %d, elements with values=keys added:\n",t->GetCurBlock());
	for(i=1;i<20;i++)
	{
		if(t->FindKeyValue(&i,val)!=0) printf("%s, ",val);
	}
	printf("\ncurrent block = %d, rewriting first 7 with values=keys+10\n",t->GetCurBlock());
	for(i=1;i<8;i++)
	{
		sprintf(val,"%d",i+10);
		t->AddRow(&i,val);
	}
	printf("deleteing 8-10 elements\n");
	for(i=8;i<11;i++)
	{
		t->DelRow(&i);
	}
	for(i=1;i<20;i++)
	{
		if(t->FindKeyValue(&i,val)!=0) printf("%s, ",val);
	}
	printf("\ncurrent block = %d, rewriting 3-10 elements with values=keys+40\n",t->GetCurBlock());
	for(i=3;i<11;i++)
	{
		sprintf(val,"%d",i+40);
		t->AddRow(&i,val);
	}
	for(i=1;i<20;i++)
	{
		if(t->FindKeyValue(&i,val)!=0) printf("%s, ",val);
	}
	printf("\ncurrent block = %d\n",t->GetCurBlock());

	strcpy(val,"new block");
	i=9;
	t->AddRow(&i,val);
	for(i=1;i<20;i++)
	{
		if(t->FindKeyValue((char*)&i,val)!=0) printf("%s, ",val);
	}
	printf("\ncurrent block = %d\n",t->GetCurBlock());
	printf("after reopen:\n");
	t->Open("big_file.test", 4, 40, 20000, 5,28,0);
	for(i=1;i<20;i++)
	{
		if(t->FindKeyValue(&i,val)!=0) printf("%s, ",val);
	}
	printf("\ncurrent block = %d\n",t->GetCurBlock());
	delete t;
}

void testJournal1(void)
{
	CycleStorage *j;
	int i;
	char *str = new char[30];
	printf("journal test 1\n");
	j = new CycleStorage("big_file.test",30,1000000,20000,true);
	for(i=1;i<33000;i++)
	{
		sprintf(str,"%d",i);
		j->AddRow(str);
	}
	printf("\nfirst 30 elements:\n");
	for(i=0;i<30;i++)
	{
		if(j->ReadRow(i,str))
			printf("%s\n",str);
	}

	printf("test of 2 classes working in 1 file together\n");
	TableBlockStorage *t = new TableBlockStorage("big_file.test", 4, 40, 20000, 5,28,0);
	char *val = new char[40];
	for(i=1;i<20;i++)
	{
		if(t->FindKeyValue((char*)&i,val)!=0) printf("%s, ",val);
	}
	printf("\ncurrent block = %d\n\n",t->GetCurBlock());

	printf("\nfirst 30 elements after deleting first 20:\n");
	for(i=0;i<20;i++)
	{
		j->DelRow(i);
	}
	for(i=0;i<30;i++)
	{
		if(j->ReadRow(i,str))
			printf("%s\n",str);
	}
	printf("\nfirst 20 after adding 10 elements\n");
	for(i=10001;i<10011;i++)
	{
		sprintf(str,"%d",i);
		j->AddRow(str);
	}
	for(i=0;i<20;i++)
	{
		if(j->ReadRow(i,str))
			printf("%s\n",str);
	}
	printf("\nthe same after reopen:\n");
	delete j;
	j = new CycleStorage();
	j->Open("big_file.test",30,1000000,20000);
	for(i=0;i<20;i++)
	{
		if(j->ReadRow(i,str))
			printf("%s\n",str);
	}
	printf("\n");
	delete t;
	delete j;
}

void testJournal2(void)
{
	CycleStorage *j;
	int i,k;
	char *str = (char*)malloc(30);
	j = new CycleStorage("big_file.test",30,1000000,20000);
	printf("journal test 2 - checking number of iterations to find head/tail\n");
	printf("iterations = %d\n",j->GetIter());
	for(i=0;i<20;i++)
	{
		for(k=1000;k<3000;k++)
		{
			sprintf(str,"%d",k);
			j->AddRow(str);
		}
		j->Open("big_file.test",30,1000000,20000);
		printf("iterations = %d\n",j->GetIter());
	}
	printf("\n");
	delete j;
}

int main(int args, char **argv)
{
	//testTable1();
	testTable2();

	testJournal1();
	testJournal2();

	return 0;
}<commit_msg>/Utilities improovements in JrnTest<commit_after>/* This file is part of the UniSet project
 * Copyright (c) 2009 Free Software Foundation, Inc.
 * Copyright (c) 2009 Ivan Donchevskiy
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 2 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
 */
// --------------------------------------------------------------------------
/*! \file
 *  \author Ivan Donchevskiy
 *  \date   $Date: 2009/07/15 15:55:00 $
 *  \version $Id: Jrn.h,v 1.0 2009/07/15 15:55:00 vpashka Exp $
 */
// --------------------------------------------------------------------------

#include "stdio.h"
#include "stdlib.h"
#include "string.h"
#include "Storages.h"
#include "UniXML.h"

void testTable1(void)
{
	char *chr=new char[20];
	char *val=new char[40];
	TableStorage *t;
	t = new TableStorage("table.test", 40, 1220, 0);
	int i;
	for(i=0;i<20;i++)
	{
		chr[0]=i;
		sprintf(val,"%d",i);
		t->AddRow(chr,val);
	}
	printf("elements with values=keys added:\n");
	for(i=0;i<40;i++)
	{
		chr[0]=i;
		if(t->FindKeyValue(chr,val)!=0) printf("%s, ",val);
	}
	printf("\n");
	for(i=9;i<15;i++)
	{
		chr[0]=i;
		t->DelRow(chr);
	}
	printf("elements with keys from 9 to 14 deleted\n");
	for(i=9;i<15;i++)
	{
		chr[0]=i;
		sprintf(val,"%d",i+40);
		t->AddRow(chr,val);
	}
	printf("elements with keys from 9 to 14 with values=key+40 added, all elements:\n");
	for(i=0;i<40;i++)
	{
		chr[0]=i;
		if(t->FindKeyValue(chr,val)!=0) printf("%s, ",val);
	}
	printf("\n");
}

bool testTable2(void)
{
	char *val=new char[40];
	TableBlockStorage *t;
	t = new TableBlockStorage();
	t->Create("big_file.test", 4, 40, 20000, 5,28,0);
	int i;
	for(i=1;i<20;i++)
	{
		if(t->FindKeyValue(&i,val)!=0) printf("%s, ",val);
	}
	printf("\n");
	if(t->GetCurBlock()!=0)
	{
		delete t;
		return false;
	}
	for(i=1;i<11;i++)
	{
		sprintf(val,"%d",i);
		t->AddRow((char*)&i,val);
	}
	if(t->GetCurBlock()!=0)
	{
		delete t;
		return false;
	}
	for(i=1;i<20;i++)
	{
		if(t->FindKeyValue(&i,val)!=0) printf("%s, ",val);
		if(val[0]==0)
		{
			delete t;
			return false;
		}
	}
	printf("\n");
	if(t->GetCurBlock()!=0)
	{
		delete t;
		return false;
	}
	for(i=1;i<8;i++)
	{
		sprintf(val,"%d",i+10);
		t->AddRow(&i,val);
	}
	printf("deleteing 8-10 elements\n");
	for(i=8;i<11;i++)
	{
		t->DelRow(&i);
	}
	for(i=1;i<20;i++)
	{
		if(t->FindKeyValue(&i,val)!=0)
		{
			printf("%s, ",val);
			if((i > 7)&&(i <11))
			{
				delete t;
				return false;
			}
		}
		if((val[0] == 0)&&(i < 8))
		{
			delete t;
			return false;
		}
	}
	printf("\nrewriting 3-10 elements with values=keys+40\n");
	if(t->GetCurBlock()!=0)
	{
		delete t;
		return false;
	}
	for(i=3;i<11;i++)
	{
		sprintf(val,"%d",i+40);
		t->AddRow(&i,val);
	}
	for(i=1;i<20;i++)
	{
		if(t->FindKeyValue(&i,val)!=0) printf("%s, ",val);
		if((atoi(val) != i+40) && (i>2) && (i<11))
		{
			delete t;
			return false;
		}
		if((atoi(val) != i+10) && (i<3))
		{
			delete t;
			return false;
		}
	}
	if(t->GetCurBlock()!=0)
	{
		delete t;
		return false;
	}

	strcpy(val,"new block");
	i=9;
	t->AddRow(&i,val);
	for(i=1;i<20;i++)
	{
		if(t->FindKeyValue((char*)&i,val)!=0) printf("%s, ",val);
	}
	if(t->GetCurBlock()!=1)
	{
		delete t;
		return false;
	}
	printf("after reopen:\n");
	t->Open("big_file.test", 4, 40, 20000, 5,28,0);
	for(i=1;i<20;i++)
	{
		if(t->FindKeyValue(&i,val)!=0) printf("%s, ",val);
	}
	if(t->GetCurBlock()!=1)
	{
		delete t;
		return false;
	}
	delete t;
	return true;
}

bool testJournal1(void)
{
	CycleStorage *j;
	int i,k=0;
	char *str = new char[30];
	printf("journal test 1\n");
	j = new CycleStorage("big_file.test",30,1000000,20000,true);
	for(i=1;i<33000;i++)
	{
		sprintf(str,"%d",i);
		j->AddRow(str);
	}
	printf("first 30 elements:\n");
	for(i=0;i<30;i++)
	{
		if(j->ReadRow(i,str))
		{
			printf("%s\n",str);
			k++;
		}
	}
	if(k < 30)
	{
		delete j;
		return false;
	}
	k = 0;

	printf("test of 2 classes working in 1 file together\n");
	TableBlockStorage *t = new TableBlockStorage("big_file.test", 4, 40, 20000, 5,28,0);
	char *val = new char[40];
	for(i=1;i<20;i++)
	{
		if(t->FindKeyValue((char*)&i,val)!=0) printf("%s, ",val);
		if((atoi(val) != i+10) && (i<3))
		{
			delete t;
			delete j;
			return false;
		}
	}

	printf("\nfirst 30 elements after deleting first 20:\n");
	for(i=0;i<20;i++)
	{
		j->DelRow(i);
	}
	for(i=0;i<30;i++)
	{
		if(j->ReadRow(i,str))
		{
			printf("%s\n",str);
			k++;
		}
	}
	if(k != 10)
	{
		delete t;
		delete j;
		return false;
	}
	k = 0;

	printf("first 20 after adding 10 elements\n");
	for(i=10001;i<10011;i++)
	{
		sprintf(str,"%d",i);
		j->AddRow(str);
	}
	for(i=0;i<20;i++)
	{
		if(j->ReadRow(i,str))
		{
			printf("%s\n",str);
			k++;
		}
	}
	if(k != 10)
	{
		delete t;
		delete j;
		return false;
	}
	k = 0;

	printf("the same after reopen:\n");
	delete j;
	j = new CycleStorage();
	j->Open("big_file.test",30,1000000,20000);
	for(i=0;i<20;i++)
	{
		if(j->ReadRow(i,str))
		{
			printf("%s\n",str);
			k++;
		}
	}
	if(k != 10)
	{
		delete t;
		delete j;
		return false;
	}
	k = 0;

	printf("the same after reopen:\n");
	delete j;
	j = new CycleStorage();
	j->Open("big_file.test",30,1000000,20000);
	for(i=0;i<20;i++)
	{
		if(j->ReadRow(i,str))
		{
			printf("%s\n",str);
			k++;
		}
	}
	if(k != 10)
	{
		delete t;
		delete j;
		//return false;
	}

	delete t;
	delete j;
	return true;
}

void testJournal2(void)
{
	CycleStorage *j;
	int i,k;
	char *str = (char*)malloc(30);
	j = new CycleStorage("big_file.test",30,1000000,20000);
	printf("journal test 2 - checking number of iterations to find head/tail\n");
	printf("iterations = %d\n",j->GetIter());
	for(i=0;i<20;i++)
	{
		for(k=1000;k<3000;k++)
		{
			sprintf(str,"%d",k);
			j->AddRow(str);
		}
		j->Open("big_file.test",30,1000000,20000);
		printf("iterations = %d\n",j->GetIter());
	}
	printf("\n");
	delete j;
}

int main(int args, char **argv)
{
	//testTable1();

	bool ok = true;
	if(testTable2())
		printf("\nTest for TableBlockStorage passed\n\n");
	else
	{
		printf("\nTest for TableBlockStorage failed\n\n");
		ok = false;
	}

	if(testJournal1())
		printf("\nTest1 for CycleStorage passed\n\n");
	else
	{
		printf("\nTest for CycleStorage failed\n\n");
		ok = false;
	}

	if(ok)
	{
		testJournal2();
		printf("TEST PASSED :)\n");
	}
	else
		printf("TEST FAILED :(\n");
	return 0;
}<|endoftext|>


<commit_before>#include "stdafx.h"

TexMan texMan;

#ifndef _MSC_VER
static GLuint LoadTextureViaOS(const char* name)
{
	jclass myview = jniEnv->FindClass(boundJavaClass);
	jmethodID method = method = jniEnv->GetStaticMethodID(myview, "loadTexture", "(Ljava/lang/String;)I");
	if (method == 0) {
		return 0;
	}
	return jniEnv->CallStaticIntMethod(myview, method, jniEnv->NewStringUTF(name));
}
#endif

#ifdef _MSC_VER
using std::min;
using std::max;
#include <gdiplus.h>
#pragma comment(lib, "gdiplus.lib")

static GLuint LoadTextureViaOS(const char* name)
{
	Gdiplus::GdiplusStartupInput gdiplusStartupInput;
	ULONG_PTR gdiplusToken;
	Gdiplus::GdiplusStartup(&gdiplusToken, &gdiplusStartupInput, nullptr);
	WCHAR wc[MAX_PATH];
	MultiByteToWideChar(CP_ACP, 0, name, -1, wc, dimof(wc));
	Gdiplus::Bitmap* image = new Gdiplus::Bitmap(wc);

	int w = (int)image->GetWidth();
	int h = (int)image->GetHeight();
	Gdiplus::Rect rc(0, 0, w, h);

	Gdiplus::BitmapData* bitmapData = new Gdiplus::BitmapData;
	image->LockBits(&rc, Gdiplus::ImageLockModeRead, PixelFormat32bppARGB, bitmapData);

	std::vector<uint32_t> col;
	col.resize(w * h);
	for (int y = 0; y < h; y++) {
		memcpy(&col[y * w], (char*)bitmapData->Scan0 + bitmapData->Stride * y, w * 4);
		for (int x = 0; x < w; x++) {
			uint32_t& c = col[y * w + x];
			c = (c & 0xff00ff00) | ((c & 0xff) << 16) | ((c & 0xff0000) >> 16);
		}
	}
	image->UnlockBits(bitmapData);
	delete bitmapData;
	delete image;
	Gdiplus::GdiplusShutdown(gdiplusToken);

	GLuint texture;
	glGenTextures(1, &texture);
	glBindTexture(GL_TEXTURE_2D, texture);
	glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR);
	glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
	glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
	glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
	glPixelStorei(GL_UNPACK_ALIGNMENT, 1);
	glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, w, h, 0, GL_RGBA, GL_UNSIGNED_BYTE, &col[0]);
	glGenerateMipmap(GL_TEXTURE_2D);
	glBindTexture(GL_TEXTURE_2D, 0);
	return texture;
}
#endif

struct DDSHeader {
	uint32_t h3[3];
	uint32_t h, w;
	uint32_t h2[2];
	uint32_t mipCnt;
	uint32_t h13[13];
	uint32_t fourcc, bitsPerPixel, rMask, gMask, bMask, aMask;
};

static void bitScanForward(uint32_t* result, uint32_t mask)
{
	//	DWORD dwd;
	//	_BitScanForward(&dwd, mask);
	//	*result = dwd;

	for (int i = 0; i < 32; i++) {
		if (mask & (1 << i)) {
			*result = i;
			return;
		}
	}
	*result = 0;
}

static GLuint CreateTextureFromRowDDS(const void* img, int size)
{
	const DDSHeader* hdr = (DDSHeader*)img;
	int w = (int)hdr->w;
	int h = (int)hdr->h;
	const uint32_t* im = (uint32_t*)img + 128 / 4;
	std::vector<uint32_t> col;
	uint32_t rShift, gShift, bShift, aShift;
	bitScanForward(&rShift, hdr->rMask);
	bitScanForward(&gShift, hdr->gMask);
	bitScanForward(&bShift, hdr->bMask);
	bitScanForward(&aShift, hdr->aMask);
	for (int y = 0; y < h; y++) {
		for (int x = 0; x < w; x++) {
			uint32_t c = *im++;
			col.push_back(
				((hdr->aMask & c) >> aShift << 24) +
				((hdr->bMask & c) >> bShift << 16) +
				((hdr->gMask & c) >> gShift << 8) +
				((hdr->rMask & c) >> rShift));
		}
	}

	GLuint texture;
	glGenTextures(1, &texture);
	glBindTexture(GL_TEXTURE_2D, texture);
	glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR);
	glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
	glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
	glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
	glPixelStorei(GL_UNPACK_ALIGNMENT, 1);
	glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, w, h, 0, GL_RGBA, GL_UNSIGNED_BYTE, &col[0]);
	glGenerateMipmap(GL_TEXTURE_2D);
	glBindTexture(GL_TEXTURE_2D, 0);
	return texture;
}

static GLuint LoadDDSTexture(const char* name)
{
	int size;
	GLuint texture = 0;
	void* img = LoadFile(name, &size);
	if (!img) {
		aflog("LoadDDSTexture failed! %s", name);
		return 0;
	}
	const DDSHeader* hdr = (DDSHeader*)img;

	GLenum format;
	int blockSize = 16;
	switch (hdr->fourcc) {
	case 0x31545844: //'1TXD':
		format = 0x83F1;	// GL_COMPRESSED_RGBA_S3TC_DXT1_EXT;
		blockSize = 8;
		break;
		//	case 0x33545844; //'3TXD':
		//		format = GL_COMPRESSED_RGBA_S3TC_DXT3_EXT;
		//		break;
		//	case 0x35545844; //'5TXD':
		//		format = GL_COMPRESSED_RGBA_S3TC_DXT5_EXT;
		//		break;
	default:
		texture = CreateTextureFromRowDDS(img, size);
		goto END;
	}

	glGenTextures(1, &texture);
	glBindTexture(GL_TEXTURE_2D, texture);
	glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR);
	glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
	glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
	glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
	{
		int texSize = blockSize * ((hdr->w + 3) / 4) * ((hdr->h + 3) / 4);
		glCompressedTexImage2D(GL_TEXTURE_2D, 0, format, hdr->w, hdr->h, 0, texSize, (char*)img + 128);
	}
	glBindTexture(GL_TEXTURE_2D, 0);

END:
	free(img);
	return texture;
}

static GLuint LoadTexture(const char* name)
{
	int len = strlen(name);
	if (len > 4 && !stricmp(name + len - 4, ".dds")) {
		return LoadDDSTexture(name);
	} else {
		return LoadTextureViaOS(name);
	}
}

static GLuint CreateWhiteTexture()
{
	uint32_t col = 0xffffffff;
	GLuint texture;
	glGenTextures(1, &texture);
	glBindTexture(GL_TEXTURE_2D, texture);
	glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
	glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
	glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
	glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
	glPixelStorei(GL_UNPACK_ALIGNMENT, 1);
	glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, 1, 1, 0, GL_RGBA, GL_UNSIGNED_BYTE, &col);
	glBindTexture(GL_TEXTURE_2D, 0);
	return texture;
}

static GLuint CreateDynamicTexture(int w, int h)
{
	GLuint texture;
	glGenTextures(1, &texture);
	glBindTexture(GL_TEXTURE_2D, texture);
	glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
	glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
	glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
	glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
	glPixelStorei(GL_UNPACK_ALIGNMENT, 1);
	glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, w, h, 0, GL_RGBA, GL_UNSIGNED_BYTE, nullptr);
	glBindTexture(GL_TEXTURE_2D, 0);
	return texture;
}

TexMan::TMID TexMan::CreateDynamicTexture(const char* name, int w, int h)
{
	auto it = nameToId.find(name);
	if (it != nameToId.end())
	{
		return it->second;
	}
	return nameToId[name] = ::CreateDynamicTexture(w, h);
}

TexMan::TMID TexMan::Create(const char *name)
{
	NameToId::iterator it = nameToId.find(name);
	if (it != nameToId.end())
	{
		return it->second;
	}
	return nameToId[name] = LoadTexture(name);
}

TexMan::TMID TexMan::CreateWhiteTexture()
{
	const std::string name = "$WHITE";
	NameToId::iterator it = nameToId.find(name);
	if (it != nameToId.end())
	{
		return it->second;
	}
	return nameToId[name] = ::CreateWhiteTexture();
}

void TexMan::Destroy()
{
	for (NameToId::iterator it = nameToId.begin(); it != nameToId.end(); ++it)
	{
		GLuint id[1] = { it->second };
		glDeleteTextures(1, id);
	}
	nameToId.clear();
}

void TexMan::Write(TMID id, const void* buf, int w, int h)
{
	glBindTexture(GL_TEXTURE_2D, id);
	glTexSubImage2D(
		GL_TEXTURE_2D,
		0,
		0,
		0,
		w,
		h,
		GL_RGBA,
		GL_UNSIGNED_BYTE,
		buf
		);
	glBindTexture(GL_TEXTURE_2D, 0);
}
<commit_msg>put min/max into Gdiplus<commit_after>#include "stdafx.h"

TexMan texMan;

#ifndef _MSC_VER
static GLuint LoadTextureViaOS(const char* name)
{
	jclass myview = jniEnv->FindClass(boundJavaClass);
	jmethodID method = method = jniEnv->GetStaticMethodID(myview, "loadTexture", "(Ljava/lang/String;)I");
	if (method == 0) {
		return 0;
	}
	return jniEnv->CallStaticIntMethod(myview, method, jniEnv->NewStringUTF(name));
}
#endif

#ifdef _MSC_VER
namespace Gdiplus {
	using std::min;
	using std::max;
}
#include <gdiplus.h>
#pragma comment(lib, "gdiplus.lib")

static GLuint LoadTextureViaOS(const char* name)
{
	Gdiplus::GdiplusStartupInput gdiplusStartupInput;
	ULONG_PTR gdiplusToken;
	Gdiplus::GdiplusStartup(&gdiplusToken, &gdiplusStartupInput, nullptr);
	WCHAR wc[MAX_PATH];
	MultiByteToWideChar(CP_ACP, 0, name, -1, wc, dimof(wc));
	Gdiplus::Bitmap* image = new Gdiplus::Bitmap(wc);

	int w = (int)image->GetWidth();
	int h = (int)image->GetHeight();
	Gdiplus::Rect rc(0, 0, w, h);

	Gdiplus::BitmapData* bitmapData = new Gdiplus::BitmapData;
	image->LockBits(&rc, Gdiplus::ImageLockModeRead, PixelFormat32bppARGB, bitmapData);

	std::vector<uint32_t> col;
	col.resize(w * h);
	for (int y = 0; y < h; y++) {
		memcpy(&col[y * w], (char*)bitmapData->Scan0 + bitmapData->Stride * y, w * 4);
		for (int x = 0; x < w; x++) {
			uint32_t& c = col[y * w + x];
			c = (c & 0xff00ff00) | ((c & 0xff) << 16) | ((c & 0xff0000) >> 16);
		}
	}
	image->UnlockBits(bitmapData);
	delete bitmapData;
	delete image;
	Gdiplus::GdiplusShutdown(gdiplusToken);

	GLuint texture;
	glGenTextures(1, &texture);
	glBindTexture(GL_TEXTURE_2D, texture);
	glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR);
	glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
	glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
	glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
	glPixelStorei(GL_UNPACK_ALIGNMENT, 1);
	glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, w, h, 0, GL_RGBA, GL_UNSIGNED_BYTE, &col[0]);
	glGenerateMipmap(GL_TEXTURE_2D);
	glBindTexture(GL_TEXTURE_2D, 0);
	return texture;
}
#endif

struct DDSHeader {
	uint32_t h3[3];
	uint32_t h, w;
	uint32_t h2[2];
	uint32_t mipCnt;
	uint32_t h13[13];
	uint32_t fourcc, bitsPerPixel, rMask, gMask, bMask, aMask;
};

static void bitScanForward(uint32_t* result, uint32_t mask)
{
	//	DWORD dwd;
	//	_BitScanForward(&dwd, mask);
	//	*result = dwd;

	for (int i = 0; i < 32; i++) {
		if (mask & (1 << i)) {
			*result = i;
			return;
		}
	}
	*result = 0;
}

static GLuint CreateTextureFromRowDDS(const void* img, int size)
{
	const DDSHeader* hdr = (DDSHeader*)img;
	int w = (int)hdr->w;
	int h = (int)hdr->h;
	const uint32_t* im = (uint32_t*)img + 128 / 4;
	std::vector<uint32_t> col;
	uint32_t rShift, gShift, bShift, aShift;
	bitScanForward(&rShift, hdr->rMask);
	bitScanForward(&gShift, hdr->gMask);
	bitScanForward(&bShift, hdr->bMask);
	bitScanForward(&aShift, hdr->aMask);
	for (int y = 0; y < h; y++) {
		for (int x = 0; x < w; x++) {
			uint32_t c = *im++;
			col.push_back(
				((hdr->aMask & c) >> aShift << 24) +
				((hdr->bMask & c) >> bShift << 16) +
				((hdr->gMask & c) >> gShift << 8) +
				((hdr->rMask & c) >> rShift));
		}
	}

	GLuint texture;
	glGenTextures(1, &texture);
	glBindTexture(GL_TEXTURE_2D, texture);
	glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR);
	glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
	glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
	glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
	glPixelStorei(GL_UNPACK_ALIGNMENT, 1);
	glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, w, h, 0, GL_RGBA, GL_UNSIGNED_BYTE, &col[0]);
	glGenerateMipmap(GL_TEXTURE_2D);
	glBindTexture(GL_TEXTURE_2D, 0);
	return texture;
}

static GLuint LoadDDSTexture(const char* name)
{
	int size;
	GLuint texture = 0;
	void* img = LoadFile(name, &size);
	if (!img) {
		aflog("LoadDDSTexture failed! %s", name);
		return 0;
	}
	const DDSHeader* hdr = (DDSHeader*)img;

	GLenum format;
	int blockSize = 16;
	switch (hdr->fourcc) {
	case 0x31545844: //'1TXD':
		format = 0x83F1;	// GL_COMPRESSED_RGBA_S3TC_DXT1_EXT;
		blockSize = 8;
		break;
		//	case 0x33545844; //'3TXD':
		//		format = GL_COMPRESSED_RGBA_S3TC_DXT3_EXT;
		//		break;
		//	case 0x35545844; //'5TXD':
		//		format = GL_COMPRESSED_RGBA_S3TC_DXT5_EXT;
		//		break;
	default:
		texture = CreateTextureFromRowDDS(img, size);
		goto END;
	}

	glGenTextures(1, &texture);
	glBindTexture(GL_TEXTURE_2D, texture);
	glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR);
	glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
	glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
	glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
	{
		int texSize = blockSize * ((hdr->w + 3) / 4) * ((hdr->h + 3) / 4);
		glCompressedTexImage2D(GL_TEXTURE_2D, 0, format, hdr->w, hdr->h, 0, texSize, (char*)img + 128);
	}
	glBindTexture(GL_TEXTURE_2D, 0);

END:
	free(img);
	return texture;
}

static GLuint LoadTexture(const char* name)
{
	int len = strlen(name);
	if (len > 4 && !stricmp(name + len - 4, ".dds")) {
		return LoadDDSTexture(name);
	} else {
		return LoadTextureViaOS(name);
	}
}

static GLuint CreateWhiteTexture()
{
	uint32_t col = 0xffffffff;
	GLuint texture;
	glGenTextures(1, &texture);
	glBindTexture(GL_TEXTURE_2D, texture);
	glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
	glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
	glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
	glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
	glPixelStorei(GL_UNPACK_ALIGNMENT, 1);
	glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, 1, 1, 0, GL_RGBA, GL_UNSIGNED_BYTE, &col);
	glBindTexture(GL_TEXTURE_2D, 0);
	return texture;
}

static GLuint CreateDynamicTexture(int w, int h)
{
	GLuint texture;
	glGenTextures(1, &texture);
	glBindTexture(GL_TEXTURE_2D, texture);
	glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
	glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
	glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
	glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
	glPixelStorei(GL_UNPACK_ALIGNMENT, 1);
	glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, w, h, 0, GL_RGBA, GL_UNSIGNED_BYTE, nullptr);
	glBindTexture(GL_TEXTURE_2D, 0);
	return texture;
}

TexMan::TMID TexMan::CreateDynamicTexture(const char* name, int w, int h)
{
	auto it = nameToId.find(name);
	if (it != nameToId.end())
	{
		return it->second;
	}
	return nameToId[name] = ::CreateDynamicTexture(w, h);
}

TexMan::TMID TexMan::Create(const char *name)
{
	NameToId::iterator it = nameToId.find(name);
	if (it != nameToId.end())
	{
		return it->second;
	}
	return nameToId[name] = LoadTexture(name);
}

TexMan::TMID TexMan::CreateWhiteTexture()
{
	const std::string name = "$WHITE";
	NameToId::iterator it = nameToId.find(name);
	if (it != nameToId.end())
	{
		return it->second;
	}
	return nameToId[name] = ::CreateWhiteTexture();
}

void TexMan::Destroy()
{
	for (NameToId::iterator it = nameToId.begin(); it != nameToId.end(); ++it)
	{
		GLuint id[1] = { it->second };
		glDeleteTextures(1, id);
	}
	nameToId.clear();
}

void TexMan::Write(TMID id, const void* buf, int w, int h)
{
	glBindTexture(GL_TEXTURE_2D, id);
	glTexSubImage2D(
		GL_TEXTURE_2D,
		0,
		0,
		0,
		w,
		h,
		GL_RGBA,
		GL_UNSIGNED_BYTE,
		buf
		);
	glBindTexture(GL_TEXTURE_2D, 0);
}
<|endoftext|>


<commit_before>/*
This file is part of Bohrium and copyright (c) 2012 the Bohrium
team <http://www.bh107.org>.

Bohrium is free software: you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as
published by the Free Software Foundation, either version 3
of the License, or (at your option) any later version.

Bohrium is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
GNU General Public License for more details.

You should have received a copy of the
GNU Lesser General Public License along with Bohrium.

If not, see <http://www.gnu.org/licenses/>.
*/

#include <string>
#include <stdexcept>
#include <boost/algorithm/string/predicate.hpp> //For iequals()
#include <bh.h>
#include "bh_fuse_price.h"

using namespace std;

namespace bohrium {

/* The default fuse model */
static const FusePriceModel default_price_model = UNIQUE_VIEWS;

/* The current selected fuse model */
static FusePriceModel selected_price_model = NUM_OF_PRICE_MODELS;

/************************************************************************/
/*************** Specific fuse model implementations ********************/
/************************************************************************/

/* Returns the bytes in a bh_view */
inline static uint64_t bytes_in_view(const bh_view &v)
{
    assert (!bh_is_constant(&v));
    return bh_nelements_nbcast(&v) * bh_type_size(v.base->type);
}

/* The cost of a kernel is the sum of unique views read and written */
static uint64_t cost_unique(const bh_ir_kernel &k)
{
    set<bh_view> unique_views;
    unique_views.insert(k.get_input_set().begin(), k.get_input_set().end());
    unique_views.insert(k.get_output_set().begin(), k.get_output_set().end());

    uint64_t sum = 0;
    for(const bh_view &v: k.get_input_set())
        sum += bytes_in_view(v);
    for(const bh_view &v: k.get_output_set())
        sum += bytes_in_view(v);
    return sum;
}
static uint64_t savings_unique(const bh_ir_kernel &k1, const bh_ir_kernel &k2)
{
    bh_ir_kernel tmp = k1;
    for(uint64_t instr_idx: k2.instr_indexes())
    {
        tmp.add_instr(instr_idx);
    }
    uint64_t old_cost = cost_unique(k1) + cost_unique(k2);
    uint64_t new_cost = cost_unique(tmp);
    assert(old_cost >= new_cost);
    return old_cost - new_cost;
}
/*
static uint64_t savings_unique_old(const bh_ir_kernel &a, const bh_ir_kernel &b)
{
    int64_t price_drop = 0;
    //Subtract inputs in 'a' that comes from 'b' or is already an input in 'b'
    for(const bh_view &i: a.get_input_set())
    {
        for(const bh_view &o: b.get_output_set())
        {
            if(bh_view_aligned(&i, &o))
                price_drop += bytes_in_view(i);
        }
        for(const bh_view &o: b.get_input_set())
        {
            if(bh_view_aligned(&i, &o))
                price_drop += bytes_in_view(i);
        }
    }
    //Subtract outputs from 'b' that are discared in 'a'
    for(const bh_view &o: b.get_output_set())
    {
        for(uint64_t a_instr_idx: a.instr_indexes())
        {
            const bh_instruction &a_instr = a.bhir->instr_list[a_instr_idx];
            if(a_instr.opcode == BH_DISCARD and a_instr.operand[0].base == o.base)
            {
                price_drop += bytes_in_view(o);
                break;
            }
        }
    }
    return price_drop;
}
*/

/* The cost of a kernel is 'number of instruction' * 3 - 'number of temp arrays' */
static uint64_t cost_temp_elemination(const bh_ir_kernel &k)
{
    return k.instr_indexes().size() * 3 - k.get_temps().size();
}
static uint64_t savings_temp_elemination(const bh_ir_kernel &k1, const bh_ir_kernel &k2)
{
    bh_ir_kernel tmp = k1;
    for(uint64_t instr_idx: k2.instr_indexes())
    {
        tmp.add_instr(instr_idx);
    }
    uint64_t old_cost = cost_temp_elemination(k1) + cost_temp_elemination(k2);
    uint64_t new_cost = cost_temp_elemination(tmp);
    assert(old_cost >= new_cost);
    return old_cost - new_cost;
}

static uint64_t cost_max_share(const bh_ir_kernel &k)
{
    if(k.instr_indexes().size() == 0)
        return 0;

    uint64_t shared_access = 0;
    for(uint64_t instr_idx=0; instr_idx < k.bhir->instr_list.size(); ++instr_idx)
    {
        const bh_instruction &instr = k.bhir->instr_list[instr_idx];
        //Check if the instruction is in this kernel
        if(std::find(k.instr_indexes().begin(), k.instr_indexes().end(), instr_idx) != k.instr_indexes().end())
            continue;

        //Let's count the number of inputs in this kernel that reads the output of 'instr'
        for(uint64_t krn_idx: k.instr_indexes())
        {
            if(krn_idx < instr_idx)
                continue;
            const bh_instruction &krn_instr = k.bhir->instr_list[krn_idx];
            const int nop = bh_operands(krn_instr.opcode);
            for(int i=1; i<nop; ++i)
            {
                const bh_view &read = krn_instr.operand[i];
                if(bh_is_constant(&read))
                    continue;
                if(read.base->nelem <= 1)
                    continue; //We ignore 1-sized arrays
                if(instr.operand[0] == read)
                    ++shared_access;
            }
        }
        //Let's count the number of shared inputs
        for(uint64_t krn_idx: k.instr_indexes())
        {
            const bh_instruction &krn_instr = k.bhir->instr_list[krn_idx];
            for(int i=1; i < bh_operands(instr.opcode); ++i)
            {
                if(bh_is_constant(&instr.operand[i]))
                    continue;
                for(int j=1; j < bh_operands(krn_instr.opcode); ++j)
                {
                    if(bh_is_constant(&krn_instr.operand[j]))
                        continue;
                    if(krn_instr.operand[j].base->nelem <= 1)
                        continue; //We ignore 1-sized arrays
                    if(instr.operand[i] == krn_instr.operand[j])
                        ++shared_access;
                }
            }
        }
    }
    return shared_access;
}
static uint64_t savings_max_share(const bh_ir_kernel &k1, const bh_ir_kernel &k2)
{
    bh_ir_kernel tmp = k1;
    for(uint64_t instr_idx: k2.instr_indexes())
    {
        tmp.add_instr(instr_idx);
    }
    uint64_t old_cost = cost_max_share(k1) + cost_max_share(k2);
    uint64_t new_cost = cost_max_share(tmp);
    assert(old_cost >= new_cost);
    return old_cost - new_cost;
}

static uint64_t cost_amos(const bh_ir_kernel &k)
{
    uint64_t N = k.bhir->instr_list.size();
    if(k.instr_indexes().size() == 0)
        return 0;

    uint64_t loop_overhead = 1;
    uint64_t not_tmp = k.get_parameters().size();
    uint64_t shared_access = cost_max_share(k);

    return loop_overhead+not_tmp*N+shared_access*N*N;
}
static uint64_t savings_amos(const bh_ir_kernel &k1, const bh_ir_kernel &k2)
{
    bh_ir_kernel tmp = k1;
    for(uint64_t instr_idx: k2.instr_indexes())
    {
        tmp.add_instr(instr_idx);
    }
    uint64_t old_cost = cost_amos(k1) + cost_amos(k2);
    uint64_t new_cost = cost_amos(tmp);
    assert(old_cost >= new_cost);
    return old_cost - new_cost;
}

/************************************************************************/
/*************** The public interface implementation ********************/
/************************************************************************/

FusePriceModel fuse_get_selected_price_model()
{
    using namespace boost;

    if(selected_price_model != NUM_OF_PRICE_MODELS)
        return selected_price_model;

    string default_model;
    fuse_price_model_text(default_price_model, default_model);

    //Check enviroment variable
    const char *env = getenv("BH_PRICE_MODEL");
    if(env != NULL)
    {
        string e(env);
        //Iterate through the 'FusePriceModel' enum and find the enum that matches
        //the enviroment variable string 'e'
        for(FusePriceModel m = UNIQUE_VIEWS; m < NUM_OF_PRICE_MODELS; m = FusePriceModel(m + 1))
        {
            string model;
            fuse_price_model_text(m, model);
            if(iequals(e, model))
            {
                return m;
            }
        }
        cerr << "[FUSE] WARNING: unknown price model: '" << e;
        cerr << "', using the default model '" << default_model << "' instead" << endl;
        setenv("BH_PRICE_MODEL", default_model.c_str(), 1);
    }
    return default_price_model;
}

void fuse_price_model_text(FusePriceModel price_model, string &output)
{
    switch(price_model)
    {
    case UNIQUE_VIEWS:
        output = "unique_views";
        break;
    case TEMP_ELEMINATION:
        output = "temp_elemination";
        break;
    case MAX_SHARE:
        output = "max_share";
        break;
    case AMOS:
        output = "amos";
        break;
    default:
        output = "unknown";
    }
}

uint64_t kernel_cost(const bh_ir_kernel &kernel)
{
    switch(selected_price_model)
    {
    case NUM_OF_PRICE_MODELS:
        selected_price_model = fuse_get_selected_price_model();
        return kernel_cost(kernel);
    case UNIQUE_VIEWS:
        return cost_unique(kernel);
    case TEMP_ELEMINATION:
        return cost_temp_elemination(kernel);
    case MAX_SHARE:
        return cost_max_share(kernel);
    case AMOS:
        return cost_amos(kernel);
    default:
        throw runtime_error("No price module is selected!");
    }
}

uint64_t kernel_cost_unique_views(const bh_ir_kernel &kernel)
{
    return cost_unique(kernel);
}

uint64_t cost_savings(const bh_ir_kernel &k1, const bh_ir_kernel &k2)
{
    switch(selected_price_model)
    {
    case NUM_OF_PRICE_MODELS:
        selected_price_model = fuse_get_selected_price_model();
        return cost_savings(k1, k2);
    case UNIQUE_VIEWS:
        return savings_unique(k1, k2);
    case TEMP_ELEMINATION:
        return savings_temp_elemination(k1, k2);
    case MAX_SHARE:
        return savings_max_share(k1, k2);
    case AMOS:
        return savings_amos(k1, k2);
    default:
        throw runtime_error("No price module is selected!");
    }
}

} //namespace bohrium
<commit_msg>fuse-price: reimplemented the MAX_SHARE price model<commit_after>/*
This file is part of Bohrium and copyright (c) 2012 the Bohrium
team <http://www.bh107.org>.

Bohrium is free software: you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as
published by the Free Software Foundation, either version 3
of the License, or (at your option) any later version.

Bohrium is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
GNU General Public License for more details.

You should have received a copy of the
GNU Lesser General Public License along with Bohrium.

If not, see <http://www.gnu.org/licenses/>.
*/

#include <string>
#include <stdexcept>
#include <boost/algorithm/string/predicate.hpp> //For iequals()
#include <bh.h>
#include "bh_fuse_price.h"

using namespace std;

namespace bohrium {

/* The default fuse model */
static const FusePriceModel default_price_model = UNIQUE_VIEWS;

/* The current selected fuse model */
static FusePriceModel selected_price_model = NUM_OF_PRICE_MODELS;

/************************************************************************/
/*************** Specific fuse model implementations ********************/
/************************************************************************/

/* Returns the bytes in a bh_view */
inline static uint64_t bytes_in_view(const bh_view &v)
{
    assert (!bh_is_constant(&v));
    return bh_nelements_nbcast(&v) * bh_type_size(v.base->type);
}

/* The cost of a kernel is the sum of unique views read and written */
static uint64_t cost_unique(const bh_ir_kernel &k)
{
    set<bh_view> unique_views;
    unique_views.insert(k.get_input_set().begin(), k.get_input_set().end());
    unique_views.insert(k.get_output_set().begin(), k.get_output_set().end());

    uint64_t sum = 0;
    for(const bh_view &v: k.get_input_set())
        sum += bytes_in_view(v);
    for(const bh_view &v: k.get_output_set())
        sum += bytes_in_view(v);
    return sum;
}
static uint64_t savings_unique(const bh_ir_kernel &k1, const bh_ir_kernel &k2)
{
    bh_ir_kernel tmp = k1;
    for(uint64_t instr_idx: k2.instr_indexes())
    {
        tmp.add_instr(instr_idx);
    }
    uint64_t old_cost = cost_unique(k1) + cost_unique(k2);
    uint64_t new_cost = cost_unique(tmp);
    assert(old_cost >= new_cost);
    return old_cost - new_cost;
}
/*
static uint64_t savings_unique_old(const bh_ir_kernel &a, const bh_ir_kernel &b)
{
    int64_t price_drop = 0;
    //Subtract inputs in 'a' that comes from 'b' or is already an input in 'b'
    for(const bh_view &i: a.get_input_set())
    {
        for(const bh_view &o: b.get_output_set())
        {
            if(bh_view_aligned(&i, &o))
                price_drop += bytes_in_view(i);
        }
        for(const bh_view &o: b.get_input_set())
        {
            if(bh_view_aligned(&i, &o))
                price_drop += bytes_in_view(i);
        }
    }
    //Subtract outputs from 'b' that are discared in 'a'
    for(const bh_view &o: b.get_output_set())
    {
        for(uint64_t a_instr_idx: a.instr_indexes())
        {
            const bh_instruction &a_instr = a.bhir->instr_list[a_instr_idx];
            if(a_instr.opcode == BH_DISCARD and a_instr.operand[0].base == o.base)
            {
                price_drop += bytes_in_view(o);
                break;
            }
        }
    }
    return price_drop;
}
*/

/* The cost of a kernel is 'number of instruction' * 3 - 'number of temp arrays' */
static uint64_t cost_temp_elemination(const bh_ir_kernel &k)
{
    return k.instr_indexes().size() * 3 - k.get_temps().size();
}
static uint64_t savings_temp_elemination(const bh_ir_kernel &k1, const bh_ir_kernel &k2)
{
    bh_ir_kernel tmp = k1;
    for(uint64_t instr_idx: k2.instr_indexes())
    {
        tmp.add_instr(instr_idx);
    }
    uint64_t old_cost = cost_temp_elemination(k1) + cost_temp_elemination(k2);
    uint64_t new_cost = cost_temp_elemination(tmp);
    assert(old_cost >= new_cost);
    return old_cost - new_cost;
}

static uint64_t cost_max_share(const bh_ir_kernel &k)
{
    if(k.instr_indexes().size() == 0)
        return 0;

    uint64_t shared_access = 0;
    for(uint64_t krn_idx: k.instr_indexes())
    {
        const bh_instruction &krn_instr = k.bhir->instr_list[krn_idx];
        if(bh_opcode_is_system(krn_instr.opcode))
            continue;
        //Find which instructions that reads and writes to arrays "visible" to
        //the instruction 'krn_idx'
        multiset<bh_view> read_access, read_access_outside;
        set<bh_view> write_access, write_access_outside;;
        for(uint64_t instr_idx=0; instr_idx < krn_idx; ++instr_idx)
        {
            const bh_instruction &instr = k.bhir->instr_list[instr_idx];
            if(bh_opcode_is_system(instr.opcode))
                continue;

            //Check if the instruction is in this kernel
            bool instr_in_kernel = false;
            if(std::find(k.instr_indexes().begin(),
                         k.instr_indexes().end(), instr_idx) != k.instr_indexes().end())
                instr_in_kernel = true;

            for(int i=bh_operands(instr.opcode)-1; i>=0; --i)
            {
                const bh_view &v = instr.operand[i];
                if(bh_is_constant(&v))
                    continue;
                if(i > 0)
                {
                    read_access.insert(v);
                    if(not instr_in_kernel)
                        read_access_outside.insert(v);
                }
                else
                {
                    write_access.insert(v);
                    read_access.erase(v);
                    if(not instr_in_kernel)
                    {
                        write_access_outside.insert(v);
                    }
                    else
                        write_access_outside.erase(v);
                    read_access_outside.erase(v);
                }
            }
        }
        for(int i=1; i < bh_operands(krn_instr.opcode); ++i)
        {
            const bh_view &v = krn_instr.operand[i];
            if(bh_is_constant(&v))
                continue;
            if(write_access_outside.find(v) != write_access_outside.end())
                shared_access++;
            shared_access += read_access_outside.count(v);
        }
    }
    return shared_access;
}
static uint64_t savings_max_share(const bh_ir_kernel &k1, const bh_ir_kernel &k2)
{
    bh_ir_kernel tmp = k1;
    for(uint64_t instr_idx: k2.instr_indexes())
    {
        tmp.add_instr(instr_idx);
    }
    uint64_t old_cost = cost_max_share(k1) + cost_max_share(k2);
    uint64_t new_cost = cost_max_share(tmp);
    assert(old_cost >= new_cost);
    return old_cost - new_cost;
}

static uint64_t cost_amos(const bh_ir_kernel &k)
{
    uint64_t N = k.bhir->instr_list.size();
    if(k.instr_indexes().size() == 0)
        return 0;

    uint64_t loop_overhead = 1;
    uint64_t not_tmp = k.get_parameters().size();
    uint64_t shared_access = cost_max_share(k);

    return loop_overhead+not_tmp*N+shared_access*N*N;
}
static uint64_t savings_amos(const bh_ir_kernel &k1, const bh_ir_kernel &k2)
{
    bh_ir_kernel tmp = k1;
    for(uint64_t instr_idx: k2.instr_indexes())
    {
        tmp.add_instr(instr_idx);
    }
    uint64_t old_cost = cost_amos(k1) + cost_amos(k2);
    uint64_t new_cost = cost_amos(tmp);
    assert(old_cost >= new_cost);
    return old_cost - new_cost;
}

/************************************************************************/
/*************** The public interface implementation ********************/
/************************************************************************/

FusePriceModel fuse_get_selected_price_model()
{
    using namespace boost;

    if(selected_price_model != NUM_OF_PRICE_MODELS)
        return selected_price_model;

    string default_model;
    fuse_price_model_text(default_price_model, default_model);

    //Check enviroment variable
    const char *env = getenv("BH_PRICE_MODEL");
    if(env != NULL)
    {
        string e(env);
        //Iterate through the 'FusePriceModel' enum and find the enum that matches
        //the enviroment variable string 'e'
        for(FusePriceModel m = UNIQUE_VIEWS; m < NUM_OF_PRICE_MODELS; m = FusePriceModel(m + 1))
        {
            string model;
            fuse_price_model_text(m, model);
            if(iequals(e, model))
            {
                return m;
            }
        }
        cerr << "[FUSE] WARNING: unknown price model: '" << e;
        cerr << "', using the default model '" << default_model << "' instead" << endl;
        setenv("BH_PRICE_MODEL", default_model.c_str(), 1);
    }
    return default_price_model;
}

void fuse_price_model_text(FusePriceModel price_model, string &output)
{
    switch(price_model)
    {
    case UNIQUE_VIEWS:
        output = "unique_views";
        break;
    case TEMP_ELEMINATION:
        output = "temp_elemination";
        break;
    case MAX_SHARE:
        output = "max_share";
        break;
    case AMOS:
        output = "amos";
        break;
    default:
        output = "unknown";
    }
}

uint64_t kernel_cost(const bh_ir_kernel &kernel)
{
    switch(selected_price_model)
    {
    case NUM_OF_PRICE_MODELS:
        selected_price_model = fuse_get_selected_price_model();
        return kernel_cost(kernel);
    case UNIQUE_VIEWS:
        return cost_unique(kernel);
    case TEMP_ELEMINATION:
        return cost_temp_elemination(kernel);
    case MAX_SHARE:
        return cost_max_share(kernel);
    case AMOS:
        return cost_amos(kernel);
    default:
        throw runtime_error("No price module is selected!");
    }
}

uint64_t kernel_cost_unique_views(const bh_ir_kernel &kernel)
{
    return cost_unique(kernel);
}

uint64_t cost_savings(const bh_ir_kernel &k1, const bh_ir_kernel &k2)
{
    switch(selected_price_model)
    {
    case NUM_OF_PRICE_MODELS:
        selected_price_model = fuse_get_selected_price_model();
        return cost_savings(k1, k2);
    case UNIQUE_VIEWS:
        return savings_unique(k1, k2);
    case TEMP_ELEMINATION:
        return savings_temp_elemination(k1, k2);
    case MAX_SHARE:
        return savings_max_share(k1, k2);
    case AMOS:
        return savings_amos(k1, k2);
    default:
        throw runtime_error("No price module is selected!");
    }
}

} //namespace bohrium
<|endoftext|>


<commit_before>#ifndef PERCEPTRON_H
#define PERCEPTRON_H

#include <sstream>
#include <vector>
#include <numeric>    // inner_product
#include <algorithm>  // transform
#include <functional> // plus, minus
#include <assert.h>
#include <cmath>      // sqrt
#include <initializer_list>

#include "randomgen.hh"
#include "classifier.hh"
#include "activation.hh"

using namespace std;

using epoch_parameter = function<double(int)>;

epoch_parameter const_epoch_parameter(double eta) {
    return [eta](int) { return eta; };
}

using Weights = vector<double>;

class APerceptron {
    /// induced local field of activation potential $v_k$, page 11
    virtual double inducedLocalField(const Input &x) = 0;
    /// neuron's output (activation function applied to the induced local field)
    virtual double output(const Input &x) = 0;
    /// neuron's weights; weights[0] is bias
    virtual Weights getWeights() const = 0;
};

class Perceptron : public APerceptron, public BinaryClassifier {
private:
    double bias;
    vector<double> weights;
    const ActivationFunction &activationFunction;

    void trainConverge_addSample(Input input, double output, double eta) {
        double y = this->output(input);
        double xfactor = eta * (output - y);
        bias += xfactor * 1.0;
        transform(
            weights.begin(), weights.end(), input.begin(),
            weights.begin() /* output */,
            [&xfactor](double w_i, double x_i) { return w_i + xfactor * x_i; });
    }

    void trainBatch_addBatch(LabeledSet batch, double eta) {
        for (auto sample : batch) {
            double d = sample.output[0]; // desired output
            Input x = sample.input;
            bias += eta * 1.0 * d;
            transform(x.begin(), x.end(), weights.begin(), weights.begin(),
                      [d, eta](double x_i, double w_i) {
                          return w_i + eta * x_i * d;
                      });
        }
    }

public:
    Perceptron(int n, const ActivationFunction &af = defaultSignum)
        : bias(0), weights(n), activationFunction(af) {}

    virtual double inducedLocalField(const Input &x) {
        assert(x.size() == weights.size());
        return inner_product(weights.begin(), weights.end(), x.begin(), bias);
    }

    virtual double output(const Input &x) {
        return activationFunction(inducedLocalField(x));
    }

    virtual bool classify(const Input &x) { return output(x) > 0; }

    /// perceptron convergence algorithm (Table 1.1)
    void trainConverge(const LabeledSet &trainSet, int epochs,
                       double eta = 1.0) {
        return trainConverge(trainSet, epochs, const_epoch_parameter(eta));
    }

    /// perceptron convergence algorithm (Table 1.1)
    void trainConverge(const LabeledSet &trainSet, int epochs,
                       epoch_parameter eta) {
        assert(trainSet.getOutputSize() == 1);
        for (int epoch = 0; epoch < epochs; ++epoch) {
            double etaval = eta(epoch);
            for (auto sample : trainSet) {
                trainConverge_addSample(sample.input, sample.output[0], etaval);
            }
        }
    }

    /// batch-training algorithm (Sec 1.6, Eq. 1.42)
    void trainBatch(const LabeledSet &trainSet, int epochs, double eta = 1.0) {
        return trainBatch(trainSet, epochs, const_epoch_parameter(eta));
    }

    /// batch-training algorithm (Sec 1.6, Eq. 1.42)
    void trainBatch(const LabeledSet &trainSet, int epochs,
                    epoch_parameter eta) {
        assert(trainSet.getOutputSize() == 1);
        assert(trainSet.getInputSize() == weights.size());
        LabeledPairPredicate isMisclassified =
            [this](const Input &in, const Output &out) {
                return (this->output(in)) * out[0] <= 0;
            };
        // \nabla J(w) = \sum_{\vec{x}(n) \in H} ( - \vec{x}(n) d(n) ) (1.40)
        // w(n+1) = w(n) - eta(n) \nabla J(w) (1.42)
        for (int epoch = 0; epoch < epochs; ++epoch) {
            double etaval = eta(epoch);
            // a new batch
            LabeledSet misclassifiedSet = trainSet.filter(isMisclassified);
            // sum cost gradient over the entire bactch
            trainBatch_addBatch(misclassifiedSet, etaval);
        }
    }

    virtual vector<double> getWeights() const {
        vector<double> biasAndWeights(weights);
        biasAndWeights.insert(biasAndWeights.begin(), bias);
        return biasAndWeights;
    }

    string fmt() {
        ostringstream ss;
        for (auto it : getWeights()) {
            ss << " " << it;
        }
        return ss.str();
    }
};

ostream &operator<<(ostream &out, const vector<double> &xs);

/**
 A basic perceptron, without built-in training facilities, with
 reasonable defaults to be used within `PerceptronsLayers`.
 */
class BasicPerceptron : public APerceptron {
private:
    Weights weights; // weights[0] is bias
    const ActivationFunction &activationFunction;

    // remember the last input and internal parameters to use them
    // again in the back-propagation step
    double lastInducedLocalField;  // v_j = \sum w_i y_i
    double lastActivationValue;    // y_j = \phi (v_j)
    double lastActivationGradient; // y_j = \phi^\prime (v_j)
    double lastLocalGradient;      // delta_j = \phi^\prime(v_j) e_j

public:
    BasicPerceptron(int n, const ActivationFunction &af = defaultTanh)
        : weights(n + 1), activationFunction(af) {}

    // one-sided Xavier initialization
    // see http://andyljones.tumblr.com/post/110998971763/
    void init(unique_ptr<rng_type> &rng) {
        int n_in = weights.size()-1;
        double sigma = n_in > 0 ? sqrt(1.0/n_in) : 1.0;
        uniform_real_distribution<double> nd(-sigma, sigma);
        generate(weights.begin(), weights.end(), [&nd, &rng] {
                    double w = nd(*rng.get());
                    return w;
                 });
    }

    virtual double inducedLocalField(const Input &x) {
        double bias = weights[0];
        auto weights_2nd = next(weights.begin());
        return inner_product(weights_2nd, weights.end(), x.begin(), bias);
    }

    virtual double output(const Input &x) {
        double v = inducedLocalField(x);
        lastInducedLocalField = v;
        lastActivationValue = activationFunction(v);
        lastActivationGradient = activationFunction.derivative(v);
        return lastActivationValue;
    }

    virtual Weights getWeights() const {
        return weights;
    }

    void adjustWeights(Weights deltaW) {
        assert(deltaW.size() == weights.size());
        for (size_t i = 0; i < weights.size(); ++i) {
            weights[i] += deltaW[i];
        }
    }

    struct BPResult {
        Weights weightCorrection;
        double localGradient;
    };

    // Page 134. Equation (4.27) defines weight correction
    //
    // $$ \Delta w_{ji} (n) =
    //    \eta
    //      \times
    //    \delta_j (n)
    //      \times
    //    y_{i} (n) $$
    //
    // where $w_{ji}$ is the synaptic weight connecting neuron $i$ to neuron $j$,
    // $\eta$ is learning rate, $delta_j (n)$ is the local [error] gradient,
    // $y_{i}$ is the input signal of the neuron $i$, $n$ is the epoch number
    //
    // The local gradient is the product of the activation function derivative
    // and the error signal.
    //
    // Return a vector of weight corrections and the local gradient value.
    //
    // The method should be called _after_ `forwardPass`
    BPResult backwardPass(const Input &inputs, double errorSignal,
                          double learningRate) {
        assert(inputs.size() + 1 == weights.size());
        size_t nInputs = weights.size();
        double localGradient = lastActivationGradient * errorSignal;
        double multiplier = learningRate * localGradient;
        Weights delta_W(nInputs, multiplier);
        for (size_t i = 0; i < inputs.size(); ++i) {
            delta_W[i + 1] *= inputs[i];
        }
        lastLocalGradient = localGradient;
        return BPResult{delta_W, localGradient};
    }
};

/// A fully connected layer of perceptrons.
class PerceptronsLayer {
private:
    unsigned int nInputs;
    unsigned int nNeurons;
    vector<BasicPerceptron> neurons;
    Output lastOutput;

public:
    PerceptronsLayer(unsigned int nInputs = 0, unsigned int nOutputs = 0,
                     const ActivationFunction &af = defaultTanh)
        : nInputs(nInputs), nNeurons(nOutputs),
          neurons(nOutputs, BasicPerceptron(nInputs, af)),
          lastOutput(nOutputs) {}

    void init(unique_ptr<rng_type> &rng) {
        for (size_t i = 0; i < neurons.size(); ++i) {
            neurons[i].init(rng);
        }
    }

    void adjustWeights(vector<Weights> weightDeltas) {
        assert(nNeurons == weightDeltas.size());
        for (size_t i = 0; i < neurons.size(); ++i) {
            neurons[i].adjustWeights(weightDeltas[i]);
        }
    }

    // Pages 132-133.
    // Return a vector of outputs.
    Output forwardPass(const Input &inputs) {
        for (auto i = 0u; i < nNeurons; ++i) {
            lastOutput[i] = neurons[i].output(inputs);
        }
        return lastOutput;
    }

    struct BPResult {
        Output propagatedErrorSignal;
        vector<Weights> weightCorrections;
    };

    // Page 134. Update synaptic weights.
    //
    // Return a vector of back-propagated error signal
    //
    // $$ e_j = \sum_k \delta_k w_{kj} $$
    //
    // where $e_j$ is an error propagated from all downstream neurons to the
    // neuron $j$, $\delta_k$ is the local gradient of the downstream neurons
    // $k$, $w_{kj}$ is the synaptic weight of the $j$-th input of the
    // downstream neuron $k$.
    //
    // The method should be called _after_ `forwardPass`
    BPResult backwardPass(const Input &inputs, const Output &errorSignals,
                          double learningRate) {
        assert(errorSignals.size() == neurons.size());
        auto eta = learningRate;
        vector<Weights> weightDeltas(0);
        Weights propagatedErrorSignals(nInputs, 0.0);
        for (auto k = 0u; k < nNeurons; ++k) {
            auto error_k = errorSignals[k];
            auto r = neurons[k].backwardPass(inputs, error_k, eta);
            Weights delta_Wk = r.weightCorrection;
            double delta_k = r.localGradient;
            Weights Wk = neurons[k].getWeights();
            for (auto j = 0u; j < nInputs; ++j) {
                propagatedErrorSignals[j] += delta_k * Wk[j + 1];
            }
            weightDeltas.push_back(delta_Wk);
        }
        return BPResult{propagatedErrorSignals, weightDeltas};
    }

    friend ostream &operator<<(ostream &out, const PerceptronsLayer &net);
};

/// Multiple layers of perceptrons stack one upon another.
class PerceptronsNetwork {
private:
    vector<PerceptronsLayer> layers;

public:
    PerceptronsNetwork(initializer_list<unsigned int> shape,
                       const ActivationFunction &af = defaultTanh)
        : layers(0) {
        auto pIn = shape.begin();
        auto pOut = next(pIn);
        for (; pOut != shape.end(); ++pIn, ++pOut) {
            PerceptronsLayer layer(*pIn, *pOut, af);
            layers.push_back(layer);
        }
    }

    void init(unique_ptr<rng_type> &rng) {
        for (auto i = 0u; i < layers.size(); ++i) {
            layers[i].init(rng);
        }
    }

    friend ostream &operator<<(ostream &out, const PerceptronsNetwork &net);
};

ostream &operator<<(ostream &out, const PerceptronsLayer &layer) {
    size_t n = layer.neurons.size();
    for (size_t j = 0; j < n; ++j) {
        if (j == 0) {
            out << "[";
        } else {
            out << " ";
        }
        out << layer.neurons[j].getWeights();
        if (j >= n - 1) {
            out << "]";
        } else {
            out << ",\n";
        }
    }
    return out;
}

ostream &operator<<(ostream &out, const PerceptronsNetwork &net) {
    for (PerceptronsLayer l : net.layers) {
        out << l << "\n";
    }
    return out;
}

ostream &operator<<(ostream &out, const vector<double> &xs) {
    int n = xs.size();
    out << "[ ";
    for (int i = 0; i < n - 1; ++i) {
        out << xs[i] << ", ";
    }
    out << xs[n - 1] << " ]";
    return out;
}

#endif /* PERCEPTRON_H */
<commit_msg>edit method comment (backwardPass)<commit_after>#ifndef PERCEPTRON_H
#define PERCEPTRON_H

#include <sstream>
#include <vector>
#include <numeric>    // inner_product
#include <algorithm>  // transform
#include <functional> // plus, minus
#include <assert.h>
#include <cmath>      // sqrt
#include <initializer_list>

#include "randomgen.hh"
#include "classifier.hh"
#include "activation.hh"

using namespace std;

using epoch_parameter = function<double(int)>;

epoch_parameter const_epoch_parameter(double eta) {
    return [eta](int) { return eta; };
}

using Weights = vector<double>;

class APerceptron {
    /// induced local field of activation potential $v_k$, page 11
    virtual double inducedLocalField(const Input &x) = 0;
    /// neuron's output (activation function applied to the induced local field)
    virtual double output(const Input &x) = 0;
    /// neuron's weights; weights[0] is bias
    virtual Weights getWeights() const = 0;
};

class Perceptron : public APerceptron, public BinaryClassifier {
private:
    double bias;
    vector<double> weights;
    const ActivationFunction &activationFunction;

    void trainConverge_addSample(Input input, double output, double eta) {
        double y = this->output(input);
        double xfactor = eta * (output - y);
        bias += xfactor * 1.0;
        transform(
            weights.begin(), weights.end(), input.begin(),
            weights.begin() /* output */,
            [&xfactor](double w_i, double x_i) { return w_i + xfactor * x_i; });
    }

    void trainBatch_addBatch(LabeledSet batch, double eta) {
        for (auto sample : batch) {
            double d = sample.output[0]; // desired output
            Input x = sample.input;
            bias += eta * 1.0 * d;
            transform(x.begin(), x.end(), weights.begin(), weights.begin(),
                      [d, eta](double x_i, double w_i) {
                          return w_i + eta * x_i * d;
                      });
        }
    }

public:
    Perceptron(int n, const ActivationFunction &af = defaultSignum)
        : bias(0), weights(n), activationFunction(af) {}

    virtual double inducedLocalField(const Input &x) {
        assert(x.size() == weights.size());
        return inner_product(weights.begin(), weights.end(), x.begin(), bias);
    }

    virtual double output(const Input &x) {
        return activationFunction(inducedLocalField(x));
    }

    virtual bool classify(const Input &x) { return output(x) > 0; }

    /// perceptron convergence algorithm (Table 1.1)
    void trainConverge(const LabeledSet &trainSet, int epochs,
                       double eta = 1.0) {
        return trainConverge(trainSet, epochs, const_epoch_parameter(eta));
    }

    /// perceptron convergence algorithm (Table 1.1)
    void trainConverge(const LabeledSet &trainSet, int epochs,
                       epoch_parameter eta) {
        assert(trainSet.getOutputSize() == 1);
        for (int epoch = 0; epoch < epochs; ++epoch) {
            double etaval = eta(epoch);
            for (auto sample : trainSet) {
                trainConverge_addSample(sample.input, sample.output[0], etaval);
            }
        }
    }

    /// batch-training algorithm (Sec 1.6, Eq. 1.42)
    void trainBatch(const LabeledSet &trainSet, int epochs, double eta = 1.0) {
        return trainBatch(trainSet, epochs, const_epoch_parameter(eta));
    }

    /// batch-training algorithm (Sec 1.6, Eq. 1.42)
    void trainBatch(const LabeledSet &trainSet, int epochs,
                    epoch_parameter eta) {
        assert(trainSet.getOutputSize() == 1);
        assert(trainSet.getInputSize() == weights.size());
        LabeledPairPredicate isMisclassified =
            [this](const Input &in, const Output &out) {
                return (this->output(in)) * out[0] <= 0;
            };
        // \nabla J(w) = \sum_{\vec{x}(n) \in H} ( - \vec{x}(n) d(n) ) (1.40)
        // w(n+1) = w(n) - eta(n) \nabla J(w) (1.42)
        for (int epoch = 0; epoch < epochs; ++epoch) {
            double etaval = eta(epoch);
            // a new batch
            LabeledSet misclassifiedSet = trainSet.filter(isMisclassified);
            // sum cost gradient over the entire bactch
            trainBatch_addBatch(misclassifiedSet, etaval);
        }
    }

    virtual vector<double> getWeights() const {
        vector<double> biasAndWeights(weights);
        biasAndWeights.insert(biasAndWeights.begin(), bias);
        return biasAndWeights;
    }

    string fmt() {
        ostringstream ss;
        for (auto it : getWeights()) {
            ss << " " << it;
        }
        return ss.str();
    }
};

ostream &operator<<(ostream &out, const vector<double> &xs);

/**
 A basic perceptron, without built-in training facilities, with
 reasonable defaults to be used within `PerceptronsLayers`.
 */
class BasicPerceptron : public APerceptron {
private:
    Weights weights; // weights[0] is bias
    const ActivationFunction &activationFunction;

    // remember the last input and internal parameters to use them
    // again in the back-propagation step
    double lastInducedLocalField;  // v_j = \sum w_i y_i
    double lastActivationValue;    // y_j = \phi (v_j)
    double lastActivationGradient; // y_j = \phi^\prime (v_j)
    double lastLocalGradient;      // delta_j = \phi^\prime(v_j) e_j

public:
    BasicPerceptron(int n, const ActivationFunction &af = defaultTanh)
        : weights(n + 1), activationFunction(af) {}

    // one-sided Xavier initialization
    // see http://andyljones.tumblr.com/post/110998971763/
    void init(unique_ptr<rng_type> &rng) {
        int n_in = weights.size()-1;
        double sigma = n_in > 0 ? sqrt(1.0/n_in) : 1.0;
        uniform_real_distribution<double> nd(-sigma, sigma);
        generate(weights.begin(), weights.end(), [&nd, &rng] {
                    double w = nd(*rng.get());
                    return w;
                 });
    }

    virtual double inducedLocalField(const Input &x) {
        double bias = weights[0];
        auto weights_2nd = next(weights.begin());
        return inner_product(weights_2nd, weights.end(), x.begin(), bias);
    }

    virtual double output(const Input &x) {
        double v = inducedLocalField(x);
        lastInducedLocalField = v;
        lastActivationValue = activationFunction(v);
        lastActivationGradient = activationFunction.derivative(v);
        return lastActivationValue;
    }

    virtual Weights getWeights() const {
        return weights;
    }

    void adjustWeights(Weights deltaW) {
        assert(deltaW.size() == weights.size());
        for (size_t i = 0; i < weights.size(); ++i) {
            weights[i] += deltaW[i];
        }
    }

    struct BPResult {
        Weights weightCorrection;
        double localGradient;
    };

    // Page 134. Equation (4.27) defines weight correction
    //
    // $$ \Delta w_{ji} (n) =
    //    \eta
    //      \times
    //    \delta_j (n)
    //      \times
    //    y_{i} (n) $$
    //
    // where $w_{ji}$ is the synaptic weight connecting neuron $i$ to neuron $j$,
    // $\eta$ is learning rate, $delta_j (n)$ is the local [error] gradient,
    // $y_{i}$ is the input signal of the neuron $i$, $n$ is the epoch number
    //
    // The local gradient is the product of the activation function derivative
    // and the error signal.
    //
    // Return a vector of weight corrections and the local gradient value.
    //
    // The method should be called _after_ `forwardPass`
    BPResult backwardPass(const Input &inputs, double errorSignal,
                          double learningRate) {
        assert(inputs.size() + 1 == weights.size());
        size_t nInputs = weights.size();
        double localGradient = lastActivationGradient * errorSignal;
        double multiplier = learningRate * localGradient;
        Weights delta_W(nInputs, multiplier);
        for (size_t i = 0; i < inputs.size(); ++i) {
            delta_W[i + 1] *= inputs[i];
        }
        lastLocalGradient = localGradient;
        return BPResult{delta_W, localGradient};
    }
};

/// A fully connected layer of perceptrons.
class PerceptronsLayer {
private:
    unsigned int nInputs;
    unsigned int nNeurons;
    vector<BasicPerceptron> neurons;
    Output lastOutput;

public:
    PerceptronsLayer(unsigned int nInputs = 0, unsigned int nOutputs = 0,
                     const ActivationFunction &af = defaultTanh)
        : nInputs(nInputs), nNeurons(nOutputs),
          neurons(nOutputs, BasicPerceptron(nInputs, af)),
          lastOutput(nOutputs) {}

    void init(unique_ptr<rng_type> &rng) {
        for (size_t i = 0; i < neurons.size(); ++i) {
            neurons[i].init(rng);
        }
    }

    void adjustWeights(vector<Weights> weightDeltas) {
        assert(nNeurons == weightDeltas.size());
        for (size_t i = 0; i < neurons.size(); ++i) {
            neurons[i].adjustWeights(weightDeltas[i]);
        }
    }

    // Pages 132-133.
    // Return a vector of outputs.
    Output forwardPass(const Input &inputs) {
        for (auto i = 0u; i < nNeurons; ++i) {
            lastOutput[i] = neurons[i].output(inputs);
        }
        return lastOutput;
    }

    struct BPResult {
        Output propagatedErrorSignal;
        vector<Weights> weightCorrections;
    };

    // Page 134. Calculate back-propagated error signal and corrections to
    // synaptic weights.
    //
    // $$ e_j = \sum_k \delta_k w_{kj} $$
    //
    // where $e_j$ is an error propagated from all downstream neurons to the
    // neuron $j$, $\delta_k$ is the local gradient of the downstream neurons
    // $k$, $w_{kj}$ is the synaptic weight of the $j$-th input of the
    // downstream neuron $k$.
    //
    // The method should be called _after_ `forwardPass`
    BPResult backwardPass(const Input &inputs, const Output &errorSignals,
                          double learningRate) {
        assert(errorSignals.size() == neurons.size());
        auto eta = learningRate;
        vector<Weights> weightDeltas(0);
        Weights propagatedErrorSignals(nInputs, 0.0);
        for (auto k = 0u; k < nNeurons; ++k) {
            auto error_k = errorSignals[k];
            auto r = neurons[k].backwardPass(inputs, error_k, eta);
            Weights delta_Wk = r.weightCorrection;
            double delta_k = r.localGradient;
            Weights Wk = neurons[k].getWeights();
            for (auto j = 0u; j < nInputs; ++j) {
                propagatedErrorSignals[j] += delta_k * Wk[j + 1];
            }
            weightDeltas.push_back(delta_Wk);
        }
        return BPResult{propagatedErrorSignals, weightDeltas};
    }

    friend ostream &operator<<(ostream &out, const PerceptronsLayer &net);
};

/// Multiple layers of perceptrons stack one upon another.
class PerceptronsNetwork {
private:
    vector<PerceptronsLayer> layers;

public:
    PerceptronsNetwork(initializer_list<unsigned int> shape,
                       const ActivationFunction &af = defaultTanh)
        : layers(0) {
        auto pIn = shape.begin();
        auto pOut = next(pIn);
        for (; pOut != shape.end(); ++pIn, ++pOut) {
            PerceptronsLayer layer(*pIn, *pOut, af);
            layers.push_back(layer);
        }
    }

    void init(unique_ptr<rng_type> &rng) {
        for (auto i = 0u; i < layers.size(); ++i) {
            layers[i].init(rng);
        }
    }

    friend ostream &operator<<(ostream &out, const PerceptronsNetwork &net);
};

ostream &operator<<(ostream &out, const PerceptronsLayer &layer) {
    size_t n = layer.neurons.size();
    for (size_t j = 0; j < n; ++j) {
        if (j == 0) {
            out << "[";
        } else {
            out << " ";
        }
        out << layer.neurons[j].getWeights();
        if (j >= n - 1) {
            out << "]";
        } else {
            out << ",\n";
        }
    }
    return out;
}

ostream &operator<<(ostream &out, const PerceptronsNetwork &net) {
    for (PerceptronsLayer l : net.layers) {
        out << l << "\n";
    }
    return out;
}

ostream &operator<<(ostream &out, const vector<double> &xs) {
    int n = xs.size();
    out << "[ ";
    for (int i = 0; i < n - 1; ++i) {
        out << xs[i] << ", ";
    }
    out << xs[n - 1] << " ]";
    return out;
}

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


<commit_before>/*
 * A filtered_socket class that offloads the actual filtering to a
 * worker thread.
 *
 * author: Max Kellermann <mk@cm4all.com>
 */

#ifndef BENG_PROXY_THREAD_SOCKET_FILTER_HXX
#define BENG_PROXY_THREAD_SOCKET_FILTER_HXX

#include "thread_job.hxx"
#include "event/DeferEvent.hxx"
#include "event/TimerEvent.hxx"
#include "SliceFifoBuffer.hxx"

#include <mutex>

typedef struct _GError GError;
struct SocketFilter;
struct FilteredSocket;
struct ThreadSocketFilter;
class ThreadQueue;

class ThreadSocketFilterHandler {
public:
    virtual ~ThreadSocketFilterHandler() {}

    /**
     * Called in the main thread before Run() is scheduled.
     */
    virtual void PreRun(ThreadSocketFilter &) {}

    /**
     * Do the work.  This is run in an unspecified worker thread.  The
     * given #ThreadSocketFilter's mutex may be used for protection.
     *
     * This method may throw exceptions, which will be forwarded to
     * BufferedSocketHandler::error().
     */
    virtual void Run(ThreadSocketFilter &f) = 0;

    /**
     * Called in the main thread after one or more run() calls have
     * finished successfully.
     */
    virtual void PostRun(ThreadSocketFilter &) {}
};

/**
 * A module for #filtered_socket that moves the filter to a thread
 * pool (see #thread_job).
 */
struct ThreadSocketFilter final : ThreadJob {
    ThreadQueue &queue;

    FilteredSocket *socket;

    /**
     * The actual filter.  If this is NULL, then this object behaves
     * just like #BufferedSocket.
     */
    ThreadSocketFilterHandler *const handler;

    BoundMethod<void()> handshake_callback{nullptr};

    /**
     * This event moves a call out of the current stack frame.  It is
     * used by ScheduleWrite() to avoid calling InvokeWrite()
     * directly.
     */
    DeferEvent defer_event;

    /**
     *
     */
    TimerEvent handshake_timeout_event;

    bool busy = false, done_pending = false;

    bool connected = true;

    /**
     * Does the handler expect more data?  It announced this by
     * returning BUFFERED_MORE.
     */
    bool expect_more = false;

    bool postponed_remaining = false;

    bool postponed_end = false;

    /**
     * Set to true when the thread queue hasn't yet released the
     * #thread_job.  The object will be destroyed in the "done"
     * callback.
     */
    bool postponed_destroy = false;

    /**
     * True when the client has called
     * filtered_socket_schedule_read().
     */
    bool want_read = false;

    /**
     * Was _schedule_read() forwarded?
     */
    bool read_scheduled = false;

    /**
     * True when the client has called
     * filtered_socket_schedule_write().
     */
    bool want_write = false;

    /**
     * True when #ThreadSocketFilterHandler's internal output buffers
     * are empty.  Set by #ThreadSocketFilterHandler::run() before
     * returning.
     *
     * Protected by #mutex.
     */
    bool drained = true;

    /**
     * True when no more input can be decrypted by
     * #ThreadSocketFilterHandler.  Will be set to true by
     * #ThreadSocketFilterHandler.
     *
     * Protected by #mutex.
     */
    bool input_eof = false;

    /**
     * Schedule the job again?  This can be used to fix up things that
     * can only be done in the main thread.
     *
     * Protected by #mutex.
     */
    bool again = false;

    /**
     * True during the initial handshake.  Will be set to false by the
     * #ThreadSocketFilterHandler.  It is used to control the
     * #handshake_timeout_event.
     *
     * Protected by #mutex.
     *
     * TODO: this is only a kludge for the stable branch.  Reimplement
     * properly.
     */
    bool handshaking = true;

    struct timeval read_timeout_buffer;
    const struct timeval *read_timeout = nullptr;

    std::mutex mutex;

    /**
     * If this is set, an exception was caught inside the thread, and
     * shall be forwarded to the main thread.
     */
    std::exception_ptr error;

    /**
     * A buffer of input data that was not yet handled by the filter.
     * It will be passed to the filter, and after that, it will go to
     * #decrypted_input.
     *
     * This gets fed from buffered_socket::input.  We need another
     * buffer because buffered_socket is not thread-safe, while this
     * buffer is protected by the #mutex.
     */
    SliceFifoBuffer encrypted_input;

    /**
     * A buffer of input data that was handled by the filter.  It will
     * be passed to the handler.
     */
    SliceFifoBuffer decrypted_input;

    /**
     * A buffer of output data that was not yet handled by the filter.
     * Once it was filtered, it will be written to #encrypted_output.
     */
    SliceFifoBuffer plain_output;

    /**
     * A buffer of output data that has been filtered already, and
     * will be written to the socket.
     */
    SliceFifoBuffer encrypted_output;

    ThreadSocketFilter(EventLoop &_event_loop,
                       ThreadQueue &queue,
                       ThreadSocketFilterHandler *handler);

    ThreadSocketFilter(const ThreadSocketFilter &) = delete;

    ~ThreadSocketFilter();

    void SetHandshakeCallback(BoundMethod<void()> callback);

    /**
     * Schedule a Run() call in a worker thread.
     */
    void Schedule();

    /**
     * @return false if the object has been destroyed
     */
    bool SubmitDecryptedInput();

    /**
     * @return the number of bytes appended to #plain_output
     */
    size_t LockWritePlainOutput(const void *data, size_t size);

    /* virtual methods from class ThreadJob */
    void Run() final;
    void Done() final;

private:
    void ClosedPrematurely();

    bool CheckRead(std::unique_lock<std::mutex> &lock);
    bool CheckWrite(std::unique_lock<std::mutex> &lock);

    void HandshakeTimeoutCallback();

    /**
     * Called in the main thread before scheduling a Run() call in a
     * worker thread.
     */
    void PreRun();

    /**
     * Called in the main thread after one or more Run() calls have
     * finished successfully.
     */
    void PostRun();

    /**
     * This event moves a call out of the current stack frame.  It is
     * used by ScheduleWrite() to avoid calling InvokeWrite()
     * directly.
     */
    void OnDeferred();
};

extern const SocketFilter thread_socket_filter;

#endif
<commit_msg>thread_socket_filter: remove obsolete GError declaration<commit_after>/*
 * A filtered_socket class that offloads the actual filtering to a
 * worker thread.
 *
 * author: Max Kellermann <mk@cm4all.com>
 */

#ifndef BENG_PROXY_THREAD_SOCKET_FILTER_HXX
#define BENG_PROXY_THREAD_SOCKET_FILTER_HXX

#include "thread_job.hxx"
#include "event/DeferEvent.hxx"
#include "event/TimerEvent.hxx"
#include "SliceFifoBuffer.hxx"

#include <mutex>

struct SocketFilter;
struct FilteredSocket;
struct ThreadSocketFilter;
class ThreadQueue;

class ThreadSocketFilterHandler {
public:
    virtual ~ThreadSocketFilterHandler() {}

    /**
     * Called in the main thread before Run() is scheduled.
     */
    virtual void PreRun(ThreadSocketFilter &) {}

    /**
     * Do the work.  This is run in an unspecified worker thread.  The
     * given #ThreadSocketFilter's mutex may be used for protection.
     *
     * This method may throw exceptions, which will be forwarded to
     * BufferedSocketHandler::error().
     */
    virtual void Run(ThreadSocketFilter &f) = 0;

    /**
     * Called in the main thread after one or more run() calls have
     * finished successfully.
     */
    virtual void PostRun(ThreadSocketFilter &) {}
};

/**
 * A module for #filtered_socket that moves the filter to a thread
 * pool (see #thread_job).
 */
struct ThreadSocketFilter final : ThreadJob {
    ThreadQueue &queue;

    FilteredSocket *socket;

    /**
     * The actual filter.  If this is NULL, then this object behaves
     * just like #BufferedSocket.
     */
    ThreadSocketFilterHandler *const handler;

    BoundMethod<void()> handshake_callback{nullptr};

    /**
     * This event moves a call out of the current stack frame.  It is
     * used by ScheduleWrite() to avoid calling InvokeWrite()
     * directly.
     */
    DeferEvent defer_event;

    /**
     *
     */
    TimerEvent handshake_timeout_event;

    bool busy = false, done_pending = false;

    bool connected = true;

    /**
     * Does the handler expect more data?  It announced this by
     * returning BUFFERED_MORE.
     */
    bool expect_more = false;

    bool postponed_remaining = false;

    bool postponed_end = false;

    /**
     * Set to true when the thread queue hasn't yet released the
     * #thread_job.  The object will be destroyed in the "done"
     * callback.
     */
    bool postponed_destroy = false;

    /**
     * True when the client has called
     * filtered_socket_schedule_read().
     */
    bool want_read = false;

    /**
     * Was _schedule_read() forwarded?
     */
    bool read_scheduled = false;

    /**
     * True when the client has called
     * filtered_socket_schedule_write().
     */
    bool want_write = false;

    /**
     * True when #ThreadSocketFilterHandler's internal output buffers
     * are empty.  Set by #ThreadSocketFilterHandler::run() before
     * returning.
     *
     * Protected by #mutex.
     */
    bool drained = true;

    /**
     * True when no more input can be decrypted by
     * #ThreadSocketFilterHandler.  Will be set to true by
     * #ThreadSocketFilterHandler.
     *
     * Protected by #mutex.
     */
    bool input_eof = false;

    /**
     * Schedule the job again?  This can be used to fix up things that
     * can only be done in the main thread.
     *
     * Protected by #mutex.
     */
    bool again = false;

    /**
     * True during the initial handshake.  Will be set to false by the
     * #ThreadSocketFilterHandler.  It is used to control the
     * #handshake_timeout_event.
     *
     * Protected by #mutex.
     *
     * TODO: this is only a kludge for the stable branch.  Reimplement
     * properly.
     */
    bool handshaking = true;

    struct timeval read_timeout_buffer;
    const struct timeval *read_timeout = nullptr;

    std::mutex mutex;

    /**
     * If this is set, an exception was caught inside the thread, and
     * shall be forwarded to the main thread.
     */
    std::exception_ptr error;

    /**
     * A buffer of input data that was not yet handled by the filter.
     * It will be passed to the filter, and after that, it will go to
     * #decrypted_input.
     *
     * This gets fed from buffered_socket::input.  We need another
     * buffer because buffered_socket is not thread-safe, while this
     * buffer is protected by the #mutex.
     */
    SliceFifoBuffer encrypted_input;

    /**
     * A buffer of input data that was handled by the filter.  It will
     * be passed to the handler.
     */
    SliceFifoBuffer decrypted_input;

    /**
     * A buffer of output data that was not yet handled by the filter.
     * Once it was filtered, it will be written to #encrypted_output.
     */
    SliceFifoBuffer plain_output;

    /**
     * A buffer of output data that has been filtered already, and
     * will be written to the socket.
     */
    SliceFifoBuffer encrypted_output;

    ThreadSocketFilter(EventLoop &_event_loop,
                       ThreadQueue &queue,
                       ThreadSocketFilterHandler *handler);

    ThreadSocketFilter(const ThreadSocketFilter &) = delete;

    ~ThreadSocketFilter();

    void SetHandshakeCallback(BoundMethod<void()> callback);

    /**
     * Schedule a Run() call in a worker thread.
     */
    void Schedule();

    /**
     * @return false if the object has been destroyed
     */
    bool SubmitDecryptedInput();

    /**
     * @return the number of bytes appended to #plain_output
     */
    size_t LockWritePlainOutput(const void *data, size_t size);

    /* virtual methods from class ThreadJob */
    void Run() final;
    void Done() final;

private:
    void ClosedPrematurely();

    bool CheckRead(std::unique_lock<std::mutex> &lock);
    bool CheckWrite(std::unique_lock<std::mutex> &lock);

    void HandshakeTimeoutCallback();

    /**
     * Called in the main thread before scheduling a Run() call in a
     * worker thread.
     */
    void PreRun();

    /**
     * Called in the main thread after one or more Run() calls have
     * finished successfully.
     */
    void PostRun();

    /**
     * This event moves a call out of the current stack frame.  It is
     * used by ScheduleWrite() to avoid calling InvokeWrite()
     * directly.
     */
    void OnDeferred();
};

extern const SocketFilter thread_socket_filter;

#endif
<|endoftext|>