<commit_before>/*
 * jcom.oscinstance
 * External for Jamoma: retrieve instance numbers or ids from osc messages
 * By Trond Lossius, Copyright  2005
 *
 * License: This code is licensed under the terms of the GNU LGPL
 * http://www.gnu.org/licenses/lgpl.html
 */

#include "Jamoma.h"

#define MAX_MESS_SIZE 2048

typedef struct _oscinstance{				///< Data Structure for this object
	t_object	ob;							///< REQUIRED: Our object
	void		*outlet0;					///< Leftmost outlet, passing osc messages
	void		*outlet1;					///< 2nd outlet, passing instance number or ID
	void		*outlet2;					///< 3rd outlet, passing the arguments of incomming OSC messages
	void		*outlet_overflow;			///< The rightmost outlet: This outlet doubles as the dumpout outlet
} t_oscinstance;

/** The jcom.oscinstance constructor */
void *oscinstance_new(t_symbol *s, long argc, t_atom *argv);

/** Provide assistance strings in the patcher window. */
void oscinstance_assist(t_oscinstance *x, void *b, long msg, long arg, char *dst);

/** Method for bang input. */
void oscinstance_bang(t_oscinstance *x);

/** Method for int input. */
void oscinstance_int(t_oscinstance *x, long n);

/** Method for int float. */
void oscinstance_float(t_oscinstance *x, double f);

/** Method for anything else, including OSC messages. */
void oscinstance_symbol(t_oscinstance *x, t_symbol *msg, long argc, t_atom *argv);
//void oscinstance_list(t_oscinstance *x, t_symbol *msg, long argc, t_atom *argv);

// Globals
t_class		*oscinstance_class;				// Required: Global pointer for our class

/************************************************************************************/
// Main() Function

int main(void)				// main recieves a copy of the Max function macros table
{
	long attrflags = 0;
	t_class *c;
	t_object *attr;

	jamoma_init();
	common_symbols_init();

	c = class_new("jcom.oscinstance",(method)oscinstance_new, (method)0L, sizeof(t_oscinstance), (method)0L, A_GIMME, 0);

	class_addmethod(c, (method)oscinstance_bang,		"bang",		0);
	class_addmethod(c, (method)oscinstance_int,			"int",		A_DEFLONG,	0L);
	class_addmethod(c, (method)oscinstance_float,		"float",	A_DEFFLOAT,	0L);
  	class_addmethod(c, (method)oscinstance_symbol,		"list", 	A_GIMME, 0L);
  	class_addmethod(c, (method)oscinstance_symbol,		"anything", A_GIMME, 0L);
	class_addmethod(c, (method)oscinstance_assist,		"assist",	A_CANT, 0L);
    class_addmethod(c, (method)object_obex_dumpout, 	"dumpout",	A_CANT,0);

	// Finalize our class
	class_register(CLASS_BOX, c);
	oscinstance_class = c;
	return 0;
}

/************************************************************************************/
// Object Life

void *oscinstance_new(t_symbol *s, long argc, t_atom *argv)
{
	t_oscinstance	*x = (t_oscinstance *)object_alloc(oscinstance_class);

	if(x){
		x->outlet_overflow = outlet_new(x, 0);		// overflow outlet
		object_obex_store((void *)x, _sym_dumpout, (object *)x->outlet_overflow);	// dumpout
		x->outlet2 = outlet_new(x, 0);				// Create Outlet
		x->outlet1 = outlet_new(x, 0);				// Create Outlet
		x->outlet0 = outlet_new(x, 0);				// Create Outlet
	}
	return (x);										// return the pointer to our new instantiation
}

/************************************************************************************/
// Methods bound to input/inlets

// Method for Assistance Messages
void oscinstance_assist(t_oscinstance *x, void *b, long msg, long arg, char *dst)
{
	if(msg==1) 						// Inlet
		strcpy(dst, "Input");
	else if(msg==2){ 				// Outlets
		if(arg == 0)
			strcpy(dst, "OSC message with instance info stripped");
		else if (arg == 1)
		 	strcpy(dst, "OSC instance number or ID");
		else
			strcpy(dst, "dumpout / overflow from non-matching input");
 	}
}

void oscinstance_bang(t_oscinstance *x)
{
	outlet_bang(x->outlet_overflow);
}

// INT INPUT
void oscinstance_int(t_oscinstance *x, long n)
{
	outlet_int(x->outlet_overflow, n);
}

// FLOAT INPUT
void oscinstance_float(t_oscinstance *x, double f)
{
	outlet_float(x->outlet_overflow, f);
}

// SYMBOL INPUT
void oscinstance_symbol(t_oscinstance *x, t_symbol *msg, long argc, t_atom *argv)
{
	char			input[MAX_MESS_SIZE];	// our input string
	char			*input2 = input;		// pointer to our input string
	char			*dot;
	char			*slash;
	t_symbol		*instance;
	char			*instanceEnd;
	t_symbol		*output;
	t_symbol		*osc = NULL;
	long			i;

	strcpy(input, msg->s_name);

	// This object only deals with OSC messages
	if(!*input2 == '/') {
		goto spillover;
	}
	input2++;								// jump past the leading slash

	dot = strchr(input2, '.');				// look for dot
	if (dot == NULL)
		goto spillover;

	slash = strchr(input2, '/');			// checking for additional osc branches

	if ( slash == 0) {
		*dot = NULL;
		osc = gensym(input2-1);				// reintroduce the leading slash
		instance = gensym(dot+1);
	}
	else {
		if ( slash<dot )
			goto spillover;					// there are instances, but not in the leading branch
		*dot = NULL;
		*slash = NULL;						// temporarily remove the slash
		instance = gensym(dot+1);
		*slash = '/';						// put slash back in
		strcat(input, slash);				// remove the instance part and concatenate
		osc = gensym(input2-1);				// reintroduce the leading slash
	}

	// Output from 3rd outlet: The arguments of the OSC message

	// We have to check what message to return.
	// The message received has no arguments:
	if (argc == 0) {
		outlet_bang(x->outlet2);
	}
	// The message received has one argument only:
	else if (argc==1) {
		// int argument
		if (argv->a_type==A_LONG)
			outlet_int(x->outlet2,argv->a_w.w_long);
		// float argument
		else if (argv->a_type==A_FLOAT)
			outlet_float(x->outlet2,argv->a_w.w_float);
		// something else
		else if (argv->a_type==A_SYM)
			outlet_anything(x->outlet2,argv->a_w.w_sym,0,0);
	}
	// There are two or more arguments: check if first is A_SYM
	else {
		if (argv->a_type==A_SYM) {
			output = argv->a_w.w_sym;
			argc--;
			argv++;
		}
		else
			output = _sym_list;
		outlet_anything(x->outlet2, output, argc , argv);
	}

	// Output from 2nd outlet: Instance. Check if the instance is an integer (long):
	i = strtol (instance->s_name,&instanceEnd,10);
	if (instance->s_name[0] != '\n' && (*instanceEnd == '\n' || *instanceEnd == '\0'))
		outlet_int(x->outlet1, i);
	else
		outlet_anything(x->outlet1, instance, NULL, 0L);

	// Output from 1st outlet: OC name with instance removed
	outlet_anything(x->outlet0, osc, NULL, 0L);
	return;

	spillover:
		outlet_anything(x->outlet_overflow, msg, argc , argv);
		return;
}

<commit_msg>corrected the labeling of the 3rd and 4th outlet <commit_after>/*
 * jcom.oscinstance
 * External for Jamoma: retrieve instance numbers or ids from osc messages
 * By Trond Lossius, Copyright  2005
 *
 * License: This code is licensed under the terms of the GNU LGPL
 * http://www.gnu.org/licenses/lgpl.html
 */

#include "Jamoma.h"

#define MAX_MESS_SIZE 2048

typedef struct _oscinstance{				///< Data Structure for this object
	t_object	ob;							///< REQUIRED: Our object
	void		*outlet0;					///< Leftmost outlet, passing osc messages
	void		*outlet1;					///< 2nd outlet, passing instance number or ID
	void		*outlet2;					///< 3rd outlet, passing the arguments of incomming OSC messages
	void		*outlet_overflow;			///< The rightmost outlet: This outlet doubles as the dumpout outlet
} t_oscinstance;

/** The jcom.oscinstance constructor */
void *oscinstance_new(t_symbol *s, long argc, t_atom *argv);

/** Provide assistance strings in the patcher window. */
void oscinstance_assist(t_oscinstance *x, void *b, long msg, long arg, char *dst);

/** Method for bang input. */
void oscinstance_bang(t_oscinstance *x);

/** Method for int input. */
void oscinstance_int(t_oscinstance *x, long n);

/** Method for int float. */
void oscinstance_float(t_oscinstance *x, double f);

/** Method for anything else, including OSC messages. */
void oscinstance_symbol(t_oscinstance *x, t_symbol *msg, long argc, t_atom *argv);
//void oscinstance_list(t_oscinstance *x, t_symbol *msg, long argc, t_atom *argv);

// Globals
t_class		*oscinstance_class;				// Required: Global pointer for our class

/************************************************************************************/
// Main() Function

int main(void)				// main recieves a copy of the Max function macros table
{
	long attrflags = 0;
	t_class *c;
	t_object *attr;

	jamoma_init();
	common_symbols_init();

	c = class_new("jcom.oscinstance",(method)oscinstance_new, (method)0L, sizeof(t_oscinstance), (method)0L, A_GIMME, 0);

	class_addmethod(c, (method)oscinstance_bang,		"bang",		0);
	class_addmethod(c, (method)oscinstance_int,			"int",		A_DEFLONG,	0L);
	class_addmethod(c, (method)oscinstance_float,		"float",	A_DEFFLOAT,	0L);
  	class_addmethod(c, (method)oscinstance_symbol,		"list", 	A_GIMME, 0L);
  	class_addmethod(c, (method)oscinstance_symbol,		"anything", A_GIMME, 0L);
	class_addmethod(c, (method)oscinstance_assist,		"assist",	A_CANT, 0L);
    class_addmethod(c, (method)object_obex_dumpout, 	"dumpout",	A_CANT,0);

	// Finalize our class
	class_register(CLASS_BOX, c);
	oscinstance_class = c;
	return 0;
}

/************************************************************************************/
// Object Life

void *oscinstance_new(t_symbol *s, long argc, t_atom *argv)
{
	t_oscinstance	*x = (t_oscinstance *)object_alloc(oscinstance_class);

	if(x){
		x->outlet_overflow = outlet_new(x, 0);		// overflow outlet
		object_obex_store((void *)x, _sym_dumpout, (object *)x->outlet_overflow);	// dumpout
		x->outlet2 = outlet_new(x, 0);				// Create Outlet
		x->outlet1 = outlet_new(x, 0);				// Create Outlet
		x->outlet0 = outlet_new(x, 0);				// Create Outlet
	}
	return (x);										// return the pointer to our new instantiation
}

/************************************************************************************/
// Methods bound to input/inlets

// Method for Assistance Messages
void oscinstance_assist(t_oscinstance *x, void *b, long msg, long arg, char *dst)
{
	if(msg==1) 						// Inlet
		strcpy(dst, "Input");
	else if(msg==2){ 				// Outlets
		if(arg == 0)
			strcpy(dst, "OSC message with instance info stripped");
		else if (arg == 1)
		 	strcpy(dst, "OSC instance number or ID");
		else if (arg == 2)
		 	strcpy(dst, "parameter value");
		else
			strcpy(dst, "dumpout / overflow from non-matching input");
 	}
}

void oscinstance_bang(t_oscinstance *x)
{
	outlet_bang(x->outlet_overflow);
}

// INT INPUT
void oscinstance_int(t_oscinstance *x, long n)
{
	outlet_int(x->outlet_overflow, n);
}

// FLOAT INPUT
void oscinstance_float(t_oscinstance *x, double f)
{
	outlet_float(x->outlet_overflow, f);
}

// SYMBOL INPUT
void oscinstance_symbol(t_oscinstance *x, t_symbol *msg, long argc, t_atom *argv)
{
	char			input[MAX_MESS_SIZE];	// our input string
	char			*input2 = input;		// pointer to our input string
	char			*dot;
	char			*slash;
	t_symbol		*instance;
	char			*instanceEnd;
	t_symbol		*output;
	t_symbol		*osc = NULL;
	long			i;

	strcpy(input, msg->s_name);

	// This object only deals with OSC messages
	if(!*input2 == '/') {
		goto spillover;
	}
	input2++;								// jump past the leading slash

	dot = strchr(input2, '.');				// look for dot
	if (dot == NULL)
		goto spillover;

	slash = strchr(input2, '/');			// checking for additional osc branches

	if ( slash == 0) {
		*dot = NULL;
		osc = gensym(input2-1);				// reintroduce the leading slash
		instance = gensym(dot+1);
	}
	else {
		if ( slash<dot )
			goto spillover;					// there are instances, but not in the leading branch
		*dot = NULL;
		*slash = NULL;						// temporarily remove the slash
		instance = gensym(dot+1);
		*slash = '/';						// put slash back in
		strcat(input, slash);				// remove the instance part and concatenate
		osc = gensym(input2-1);				// reintroduce the leading slash
	}

	// Output from 3rd outlet: The arguments of the OSC message

	// We have to check what message to return.
	// The message received has no arguments:
	if (argc == 0) {
		outlet_bang(x->outlet2);
	}
	// The message received has one argument only:
	else if (argc==1) {
		// int argument
		if (argv->a_type==A_LONG)
			outlet_int(x->outlet2,argv->a_w.w_long);
		// float argument
		else if (argv->a_type==A_FLOAT)
			outlet_float(x->outlet2,argv->a_w.w_float);
		// something else
		else if (argv->a_type==A_SYM)
			outlet_anything(x->outlet2,argv->a_w.w_sym,0,0);
	}
	// There are two or more arguments: check if first is A_SYM
	else {
		if (argv->a_type==A_SYM) {
			output = argv->a_w.w_sym;
			argc--;
			argv++;
		}
		else
			output = _sym_list;
		outlet_anything(x->outlet2, output, argc , argv);
	}

	// Output from 2nd outlet: Instance. Check if the instance is an integer (long):
	i = strtol (instance->s_name,&instanceEnd,10);
	if (instance->s_name[0] != '\n' && (*instanceEnd == '\n' || *instanceEnd == '\0'))
		outlet_int(x->outlet1, i);
	else
		outlet_anything(x->outlet1, instance, NULL, 0L);

	// Output from 1st outlet: OC name with instance removed
	outlet_anything(x->outlet0, osc, NULL, 0L);
	return;

	spillover:
		outlet_anything(x->outlet_overflow, msg, argc , argv);
		return;
}

<|endoftext|>


<commit_before>/*
 * Licensed to the Apache Software Foundation (ASF) under one or more
 * contributor license agreements.  See the NOTICE file distributed with
 * this work for additional information regarding copyright ownership.
 * The ASF licenses this file to You 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 "ServerSocket.h"
#include "SocketError.h"

#ifdef HAVE_WINSOCK2_H
    #include <Winsock2.h>
    #include <Ws2tcpip.h>
    #include <sys/stat.h>
    #define stat _stat
#else
    #include <unistd.h>
    #include <netdb.h>
    #include <fcntl.h>
    #include <sys/file.h>
    #include <sys/socket.h>
    #include <netinet/in.h>
    #include <arpa/inet.h>
    #include <string.h>
#endif

#include <stdio.h>
#include <stdlib.h>
#include <stdarg.h>
#include <ctype.h>
#include <sys/types.h>
#include <assert.h>
#include <string>

using namespace activemq::network;

#ifdef HAVE_WINSOCK2_H

    // Static socket initializer needed for winsock

    ServerSocket::StaticServerSocketInitializer::StaticServerSocketInitializer () {
        socketInitError = NULL;
        const WORD version_needed = MAKEWORD(2,2); // lo-order byte: major version
        WSAData temp;
        if( WSAStartup(version_needed, &temp )){
           clear();
               socketInitError = new SocketException ( __FILE__, __LINE__,
                   "winsock.dll was not found");
        }
    }
    ServerSocket::StaticServerSocketInitializer::~StaticServerSocketInitializer () {
        clear();
        WSACleanup();
    }

    // Create static instance of the socket initializer.
    ServerSocket::StaticServerSocketInitializer
        ServerSocket::staticSocketInitializer;

#endif

////////////////////////////////////////////////////////////////////////////////
ServerSocket::ServerSocket()
{
    socketHandle = Socket::INVALID_SOCKET_HANDLE;

#if defined(HAVE_WINSOCK2_H)
    if( ServerSocket::staticSocketInitializer.getSocketInitError() != NULL ) {
        throw *ServerSocket::staticSocketInitializer.getSocketInitError();
    }
#endif
}

////////////////////////////////////////////////////////////////////////////////
ServerSocket::~ServerSocket()
{
    // No shutdown, just close - dont want blocking destructor.
    close();
}

////////////////////////////////////////////////////////////////////////////////
void ServerSocket::bind( const char* host, int port ) throw ( SocketException )
{
    bind (host, port, SOMAXCONN);
}

////////////////////////////////////////////////////////////////////////////////
void ServerSocket::bind( const char* host,
                         int port,
                         int backlog ) throw ( SocketException )
{
    if(isBound()) {
        throw SocketException ( __FILE__, __LINE__,
            "ServerSocket::bind - Socket already bound" );
    }

    // Create the socket.
    socketHandle = ::socket(AF_INET, SOCK_STREAM, 0 );
    if( socketHandle < 0) {
        socketHandle = Socket::INVALID_SOCKET_HANDLE;
            throw SocketException( __FILE__, __LINE__, SocketError::getErrorString().c_str());
    }

    // Verify the port value.
    if( port <= 0 || port > 65535 ) {
        throw SocketException( __FILE__, __LINE__,
            "ServerSocket::bind - Port out of range: %d", port );
    }

    sockaddr_in bind_addr;
    bind_addr.sin_family = AF_INET;
    bind_addr.sin_port = htons((short)port);
    bind_addr.sin_addr.s_addr = 0; // To be set later down...
    memset(&bind_addr.sin_zero, 0, sizeof(bind_addr.sin_zero));
	int status;

    // Resolve name
#if defined(HAVE_STRUCT_ADDRINFO)
    ::addrinfo hints;
    memset(&hints, 0, sizeof(addrinfo));
    hints.ai_family = PF_INET;
    struct addrinfo *res_ptr = NULL;
    status = ::getaddrinfo(host, NULL, &hints, &res_ptr);
    if( status != 0 || res_ptr == NULL) {
        throw SocketException( __FILE__, __LINE__, SocketError::getErrorString().c_str() );
    }
    assert(res_ptr->ai_addr->sa_family == AF_INET);
    // Porting: On both 32bit and 64 bit systems that we compile to soo far, sin_addr is a 32 bit value, not an unsigned long.
    assert(sizeof(((sockaddr_in*)res_ptr->ai_addr)->sin_addr.s_addr) == 4);
    bind_addr.sin_addr.s_addr = ((sockaddr_in*)res_ptr->ai_addr)->sin_addr.s_addr;
    freeaddrinfo(res_ptr);
#else
	struct ::hostent *he = ::gethostbyname(host);
	if( he == NULL ) {
        throw SocketException( __FILE__, __LINE__, "Failed to resolve hostname" );
	}
	bind_addr.sin_addr.s_addr = *((in_addr_t *)he->h_addr);
#endif

    // Set the socket to reuse the address.
    int value = 1;
    ::setsockopt(socketHandle, SOL_SOCKET, SO_REUSEADDR, (char*)&value, sizeof(int) );

    status = ::bind(socketHandle,
             reinterpret_cast<sockaddr*>(&bind_addr), sizeof( bind_addr ));

    if( status < 0 ){
        close();
        throw SocketException ( __FILE__, __LINE__,
            "ServerSocket::bind - %s", SocketError::getErrorString().c_str() );
    }
    status = ::listen( socketHandle, (int)backlog );
    if( status < 0 ) {
        close();
        throw SocketException( __FILE__, __LINE__, SocketError::getErrorString().c_str() );
    }
}

////////////////////////////////////////////////////////////////////////////////
void ServerSocket::close() throw (cms::CMSException){

    if( isBound() ) {

        #if !defined(HAVE_WINSOCK2_H)
            ::close( socketHandle );
        #else
            ::closesocket( socketHandle );
        #endif

        socketHandle = Socket::INVALID_SOCKET_HANDLE;
    }
}

////////////////////////////////////////////////////////////////////////////////
bool ServerSocket::isBound() const {
    return this->socketHandle != Socket::INVALID_SOCKET_HANDLE;
}

////////////////////////////////////////////////////////////////////////////////
Socket* ServerSocket::accept() throw (SocketException)
{
    struct sockaddr_in temp;

    #if !defined(HAVE_WINSOCK2_H)
        socklen_t temp_len = sizeof( sockaddr_in );
    #else
        int temp_len = sizeof( sockaddr_in );
    #endif

    SocketHandle ss_socket_handle =
        ::accept( socketHandle, reinterpret_cast<struct sockaddr*>(&temp), &temp_len );
    if( ss_socket_handle < 0 ) {
        throw SocketException( __FILE__, __LINE__,
            "ServerSocket::accept- %s", SocketError::getErrorString().c_str() );
    }

    return new TcpSocket( ss_socket_handle );
}

<commit_msg>AMQCPP-107 - adding handling of signal interruptions to ServerSocket<commit_after>/*
 * Licensed to the Apache Software Foundation (ASF) under one or more
 * contributor license agreements.  See the NOTICE file distributed with
 * this work for additional information regarding copyright ownership.
 * The ASF licenses this file to You 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 "ServerSocket.h"
#include "SocketError.h"

#ifdef HAVE_WINSOCK2_H
    #include <Winsock2.h>
    #include <Ws2tcpip.h>
    #include <sys/stat.h>
    #define stat _stat
#else
    #include <unistd.h>
    #include <netdb.h>
    #include <fcntl.h>
    #include <sys/file.h>
    #include <sys/socket.h>
    #include <netinet/in.h>
    #include <arpa/inet.h>
    #include <string.h>
#endif

#include <stdio.h>
#include <stdlib.h>
#include <stdarg.h>
#include <ctype.h>
#include <sys/types.h>
#include <assert.h>
#include <string>

using namespace activemq::network;

#ifdef HAVE_WINSOCK2_H

    // Static socket initializer needed for winsock

    ServerSocket::StaticServerSocketInitializer::StaticServerSocketInitializer () {
        socketInitError = NULL;
        const WORD version_needed = MAKEWORD(2,2); // lo-order byte: major version
        WSAData temp;
        if( WSAStartup(version_needed, &temp )){
           clear();
               socketInitError = new SocketException ( __FILE__, __LINE__,
                   "winsock.dll was not found");
        }
    }
    ServerSocket::StaticServerSocketInitializer::~StaticServerSocketInitializer () {
        clear();
        WSACleanup();
    }

    // Create static instance of the socket initializer.
    ServerSocket::StaticServerSocketInitializer
        ServerSocket::staticSocketInitializer;

#endif

////////////////////////////////////////////////////////////////////////////////
ServerSocket::ServerSocket()
{
    socketHandle = Socket::INVALID_SOCKET_HANDLE;

#if defined(HAVE_WINSOCK2_H)
    if( ServerSocket::staticSocketInitializer.getSocketInitError() != NULL ) {
        throw *ServerSocket::staticSocketInitializer.getSocketInitError();
    }
#endif
}

////////////////////////////////////////////////////////////////////////////////
ServerSocket::~ServerSocket()
{
    // No shutdown, just close - dont want blocking destructor.
    close();
}

////////////////////////////////////////////////////////////////////////////////
void ServerSocket::bind( const char* host, int port ) throw ( SocketException )
{
    bind (host, port, SOMAXCONN);
}

////////////////////////////////////////////////////////////////////////////////
void ServerSocket::bind( const char* host,
                         int port,
                         int backlog ) throw ( SocketException )
{
    if(isBound()) {
        throw SocketException ( __FILE__, __LINE__,
            "ServerSocket::bind - Socket already bound" );
    }

    // Create the socket.
    socketHandle = ::socket(AF_INET, SOCK_STREAM, 0 );
    if( socketHandle < 0) {
        socketHandle = Socket::INVALID_SOCKET_HANDLE;
            throw SocketException( __FILE__, __LINE__, SocketError::getErrorString().c_str());
    }

    // Verify the port value.
    if( port <= 0 || port > 65535 ) {
        throw SocketException( __FILE__, __LINE__,
            "ServerSocket::bind - Port out of range: %d", port );
    }

    sockaddr_in bind_addr;
    bind_addr.sin_family = AF_INET;
    bind_addr.sin_port = htons((short)port);
    bind_addr.sin_addr.s_addr = 0; // To be set later down...
    memset(&bind_addr.sin_zero, 0, sizeof(bind_addr.sin_zero));
	int status;

    // Resolve name
#if defined(HAVE_STRUCT_ADDRINFO)
    ::addrinfo hints;
    memset(&hints, 0, sizeof(addrinfo));
    hints.ai_family = PF_INET;
    struct addrinfo *res_ptr = NULL;
    status = ::getaddrinfo(host, NULL, &hints, &res_ptr);
    if( status != 0 || res_ptr == NULL) {
        throw SocketException( __FILE__, __LINE__, SocketError::getErrorString().c_str() );
    }
    assert(res_ptr->ai_addr->sa_family == AF_INET);
    // Porting: On both 32bit and 64 bit systems that we compile to soo far, sin_addr is a 32 bit value, not an unsigned long.
    assert(sizeof(((sockaddr_in*)res_ptr->ai_addr)->sin_addr.s_addr) == 4);
    bind_addr.sin_addr.s_addr = ((sockaddr_in*)res_ptr->ai_addr)->sin_addr.s_addr;
    freeaddrinfo(res_ptr);
#else
	struct ::hostent *he = ::gethostbyname(host);
	if( he == NULL ) {
        throw SocketException( __FILE__, __LINE__, "Failed to resolve hostname" );
	}
	bind_addr.sin_addr.s_addr = *((in_addr_t *)he->h_addr);
#endif

    // Set the socket to reuse the address.
    int value = 1;
    ::setsockopt(socketHandle, SOL_SOCKET, SO_REUSEADDR, (char*)&value, sizeof(int) );

    status = ::bind(socketHandle,
             reinterpret_cast<sockaddr*>(&bind_addr), sizeof( bind_addr ));

    if( status < 0 ){
        close();
        throw SocketException ( __FILE__, __LINE__,
            "ServerSocket::bind - %s", SocketError::getErrorString().c_str() );
    }
    status = ::listen( socketHandle, (int)backlog );
    if( status < 0 ) {
        close();
        throw SocketException( __FILE__, __LINE__, SocketError::getErrorString().c_str() );
    }
}

////////////////////////////////////////////////////////////////////////////////
void ServerSocket::close() throw (cms::CMSException){

    if( isBound() ) {

        #if !defined(HAVE_WINSOCK2_H)
            ::close( socketHandle );
        #else
            ::closesocket( socketHandle );
        #endif

        socketHandle = Socket::INVALID_SOCKET_HANDLE;
    }
}

////////////////////////////////////////////////////////////////////////////////
bool ServerSocket::isBound() const {
    return this->socketHandle != Socket::INVALID_SOCKET_HANDLE;
}

////////////////////////////////////////////////////////////////////////////////
Socket* ServerSocket::accept() throw (SocketException)
{
    struct sockaddr_in temp;

    #if !defined(HAVE_WINSOCK2_H)
        socklen_t temp_len = sizeof( sockaddr_in );
    #else
        int temp_len = sizeof( sockaddr_in );
    #endif

    SocketHandle ss_socket_handle = NULL;

    // Loop to ignore any signal interruptions that occur during the operation.
    do {

        ss_socket_handle = ::accept( socketHandle,
                                     reinterpret_cast<struct sockaddr*>(&temp),
                                     &temp_len );

    } while( ss_socket_handle < 0 &&
             SocketError::getErrorCode() == SocketError::INTERRUPTED );

    if( ss_socket_handle < 0 ) {
        throw SocketException( __FILE__, __LINE__,
            "ServerSocket::accept- %s", SocketError::getErrorString().c_str() );
    }

    return new TcpSocket( ss_socket_handle );
}

<|endoftext|>


<commit_before>#include <dlfcn.h>
#include <cstdio>
#include <cstring>
#include <unistd.h>
#include <cstdlib>
#include <termios.h>
#include <sys/ioctl.h>

#include <iostream>
#include <fstream>
#include <vector>
#include <array>
#include <string>
#include <map>
#include <functional>
#include <experimental/optional>

#include "History.hpp"

#define cursor_forward(x) printf("\033[%dC", static_cast<int>(x))
#define cursor_backward(x) printf("\033[%dD", static_cast<int>(x))

thread_local std::array<char, 1024> lineBuffer;
thread_local auto lineBufferPos = lineBuffer.begin();

thread_local std::string printBuffer;
thread_local std::string::iterator printBufferPos;

using namespace yb;
thread_local History history;
thread_local History::const_iterator historyPos;

static char arrowIndicator = 0;

using ReadSignature = ssize_t (*)(int, void*, size_t);
using Char = unsigned char;
using CharOpt = std::experimental::optional<Char>;

CharOpt newlineHandler(Char);
CharOpt tabHandler(Char);
CharOpt backspaceHandler(Char);
CharOpt regularCHarHandler(Char);
CharOpt arrowHandler1(Char);
CharOpt arrowHandler2(Char);
CharOpt arrowHandler3(Char);

thread_local std::map<Char, std::function<CharOpt(Char)>> handlers = {
    {0x06, tabHandler},
    {0x0d, newlineHandler},
    {0x17, newlineHandler}, // TODO: this should delete one word
    {0x1b, arrowHandler1},
    {0x5b, arrowHandler2},
    {0x43, arrowHandler3},
    {0x7f, backspaceHandler}
};

int getCursorPosition(int &x, int &y) {
    int retVal = -1;
    fd_set stdInSet;
    struct timeval time;
    struct termios rawTermios, oldTermios;

    tcgetattr(STDIN_FILENO, &oldTermios);
    rawTermios = oldTermios;
    rawTermios.c_lflag &= ~ICANON;
    rawTermios.c_lflag &= ~ECHO;
    tcsetattr(STDIN_FILENO, TCSANOW, &rawTermios);

    printf("\033[6n");
    fflush(stdout);

    FD_ZERO(&stdInSet);
    FD_SET(STDIN_FILENO, &stdInSet);
    time.tv_sec = 0;
    time.tv_usec = 100000;

    if (select(STDIN_FILENO + 1, &stdInSet, NULL, NULL, &time) == 1)
        if (scanf("\033[%d;%dR", &x, &y) == 2)
            retVal = 0;

    tcsetattr(STDIN_FILENO, TCSADRAIN, &oldTermios);

    return retVal;
}

int getTerminalWidth() {
    struct winsize w;
    ioctl(0, TIOCGWINSZ, &w);
    return w.ws_col;
}

void clearTerminalLine() {
    // TODO: get info about terminal width and current cursor position
    // and fix below loops
    int col, row, width;
    if (getCursorPosition(row, col)) return;
    width = getTerminalWidth();
    for (int i = 0; i < width - col; i++)
        printf(" ");
    fflush(stdout);
    for (int i = 0; i < width - col; i++)
        cursor_backward(1);
}

std::string findCompletion(History::const_iterator start, const std::string &pattern) {
    for (auto it = start - 1; it > history.begin(); it--) {
        if (it->compare(0, pattern.length(), pattern) == 0) {
            historyPos = it;
            return *it;
        }
    }

    historyPos = history.end();
    return pattern;
}

void printCompletion(History::const_iterator startIterator, int offset) {
    std::string pattern(lineBuffer.data());
    auto completion = findCompletion(startIterator, pattern);

    clearTerminalLine();

    if (offset)
        cursor_forward(offset);
    printf("\e[1;30m%s\e[0m", completion.c_str() + pattern.length());

    cursor_backward(completion.length() - pattern.length() + offset);
    fflush(stdout);
}

CharOpt newlineHandler(Char) {
    lineBuffer.fill(0);
    lineBufferPos = lineBuffer.begin();
    return {};
}

CharOpt backspaceHandler(Char) {
    if (lineBufferPos != lineBuffer.begin()) {
        *(--lineBufferPos) = 0;
    }

    return {};
}

CharOpt regularCharHandler(Char c) {
    *lineBufferPos = c;
    lineBufferPos++;

    printCompletion(history.end(), 1);

    return {};
}

CharOpt tabHandler(Char) {
    printCompletion(historyPos, 0);
    return Char{0}; // TODO: this does not seem to work.
}

CharOpt arrowHandler1(Char) {
    arrowIndicator = 1;
    return {};
}

CharOpt arrowHandler2(Char c) {
    if (arrowIndicator == 1) {
        arrowIndicator = 2;
        return {};
    }
    else {
        return regularCharHandler(c);
    }
}

CharOpt arrowHandler3(Char c) {
    CharOpt return_value = {};
    if (arrowIndicator == 2) {
        arrowIndicator = 0;
    }
    else {
        return_value = regularCharHandler(c);
    }
    try {
        printBuffer = historyPos->substr(lineBufferPos - lineBuffer.begin());
        printBufferPos = printBuffer.begin();
    } catch (...) {
        // FIXME:
    }

    return return_value;
}

static unsigned char yebash(unsigned char c) {

    // TODO: uncomment later
    //if (!getenv("YEBASH"))
    //    return;
    history.read(std::string{getenv("HOME")} + "/.bash_history");

    auto handler = handlers[c];

    // TODO(Mrokkk): There would be a problem with processing all escape codes
    // that bash is using (e.g. ctrl+r for history searching, arrows for moving, etc.).
    // It would be thoughtful if we just disable our yebash if any of these codes would
    // occur and reenable it after a newline
    CharOpt cReturned = c;
    if (handler) {
        handler(c);
    }
    else {
        if (c < 0x20) {
            newlineHandler(c);
        }
        else {
            regularCharHandler(c);
        }
    }

    if (static_cast<bool>(cReturned)) {
        return cReturned.value();
    }

    return c;
}

ssize_t read(int fd, void *buf, size_t count) {

    ssize_t returnValue;
    static thread_local ReadSignature realRead = nullptr;

    if (fd == 0) { // TODO: make it look good
        if (printBuffer.length()) {
            // Return printBuffer to bash one char at time
            *reinterpret_cast<char *>(buf) = *printBufferPos;
            *lineBufferPos++ =  *printBufferPos++;
            if (printBufferPos == printBuffer.end()) {
                printBuffer.erase(printBuffer.begin(), printBuffer.end());
            }
            return 1;
        }
    }

    if (!realRead)
        realRead = reinterpret_cast<ReadSignature>(dlsym(RTLD_NEXT, "read"));

    returnValue = realRead(fd, buf, count);

    if (fd == 0 && isatty(fileno(stdin)))
        *reinterpret_cast<unsigned char *>(buf) = yebash(*reinterpret_cast<unsigned char *>(buf));

    return returnValue;
}

<commit_msg>Improve clearing line<commit_after>#include <dlfcn.h>
#include <cstdio>
#include <cstring>
#include <unistd.h>
#include <cstdlib>
#include <termios.h>
#include <sys/ioctl.h>

#include <iostream>
#include <fstream>
#include <vector>
#include <array>
#include <string>
#include <map>
#include <functional>
#include <experimental/optional>

#include "History.hpp"

#define cursor_forward(x) printf("\033[%dC", static_cast<int>(x))
#define cursor_backward(x) printf("\033[%dD", static_cast<int>(x))

thread_local std::array<char, 1024> lineBuffer;
thread_local auto lineBufferPos = lineBuffer.begin();

thread_local std::string printBuffer;
thread_local std::string::iterator printBufferPos;

using namespace yb;
thread_local History history;
thread_local History::const_iterator historyPos;

static char arrowIndicator = 0;

using ReadSignature = ssize_t (*)(int, void*, size_t);
using Char = unsigned char;
using CharOpt = std::experimental::optional<Char>;

CharOpt newlineHandler(Char);
CharOpt tabHandler(Char);
CharOpt backspaceHandler(Char);
CharOpt regularCHarHandler(Char);
CharOpt arrowHandler1(Char);
CharOpt arrowHandler2(Char);
CharOpt arrowHandler3(Char);

thread_local std::map<Char, std::function<CharOpt(Char)>> handlers = {
    {0x06, tabHandler},
    {0x0d, newlineHandler},
    {0x17, newlineHandler}, // TODO: this should delete one word
    {0x1b, arrowHandler1},
    {0x5b, arrowHandler2},
    {0x43, arrowHandler3},
    {0x7f, backspaceHandler}
};

int getCursorPosition() {
    int retVal = 0, x, y;
    fd_set stdInSet;
    struct timeval time;
    struct termios rawTermios, oldTermios;

    tcgetattr(STDIN_FILENO, &oldTermios);
    rawTermios = oldTermios;
    rawTermios.c_lflag &= ~ICANON;
    rawTermios.c_lflag &= ~ECHO;
    tcsetattr(STDIN_FILENO, TCSANOW, &rawTermios);

    printf("\033[6n");
    fflush(stdout);

    FD_ZERO(&stdInSet);
    FD_SET(STDIN_FILENO, &stdInSet);
    time.tv_sec = 0;
    time.tv_usec = 100000;

    if (select(STDIN_FILENO + 1, &stdInSet, NULL, NULL, &time) == 1)
        if (scanf("\033[%d;%dR", &x, &y) == 2)
            retVal = y;

    tcsetattr(STDIN_FILENO, TCSADRAIN, &oldTermios);

    return retVal;
}

int getTerminalWidth() {
    struct winsize w;
    ioctl(0, TIOCGWINSZ, &w);
    return w.ws_col;
}

void clearTerminalLine() {
    int pos, width;
    if (!(pos = getCursorPosition())) return;
    width = getTerminalWidth();
    for (int i = 0; i < width - pos; i++)
        printf(" ");
    fflush(stdout);
    for (int i = 0; i < width - pos; i++)
        cursor_backward(1);
}

std::string findCompletion(History::const_iterator start, const std::string &pattern) {
    for (auto it = start - 1; it > history.begin(); it--) {
        if (it->compare(0, pattern.length(), pattern) == 0) {
            historyPos = it;
            return *it;
        }
    }

    historyPos = history.end();
    return pattern;
}

void printCompletion(History::const_iterator startIterator, int offset) {
    std::string pattern(lineBuffer.data());
    auto completion = findCompletion(startIterator, pattern);

    clearTerminalLine();

    if (offset)
        cursor_forward(offset);
    printf("\e[1;30m%s\e[0m", completion.c_str() + pattern.length());

    cursor_backward(completion.length() - pattern.length() + offset);
    fflush(stdout);
}

CharOpt newlineHandler(Char) {
    lineBuffer.fill(0);
    lineBufferPos = lineBuffer.begin();
    return {};
}

CharOpt backspaceHandler(Char) {
    if (lineBufferPos != lineBuffer.begin()) {
        *(--lineBufferPos) = 0;
    }

    return {};
}

CharOpt regularCharHandler(Char c) {
    *lineBufferPos = c;
    lineBufferPos++;

    printCompletion(history.end(), 1);

    return {};
}

CharOpt tabHandler(Char) {
    printCompletion(historyPos, 0);
    return Char{0}; // TODO: this does not seem to work.
}

CharOpt arrowHandler1(Char) {
    arrowIndicator = 1;
    return {};
}

CharOpt arrowHandler2(Char c) {
    if (arrowIndicator == 1) {
        arrowIndicator = 2;
        return {};
    }
    else {
        return regularCharHandler(c);
    }
}

CharOpt arrowHandler3(Char c) {
    CharOpt return_value = {};
    if (arrowIndicator == 2) {
        arrowIndicator = 0;
    }
    else {
        return_value = regularCharHandler(c);
    }
    try {
        printBuffer = historyPos->substr(lineBufferPos - lineBuffer.begin());
        printBufferPos = printBuffer.begin();
    } catch (...) {
        // FIXME:
    }

    return return_value;
}

static unsigned char yebash(unsigned char c) {

    // TODO: uncomment later
    //if (!getenv("YEBASH"))
    //    return;
    history.read(std::string{getenv("HOME")} + "/.bash_history");

    auto handler = handlers[c];

    // TODO(Mrokkk): There would be a problem with processing all escape codes
    // that bash is using (e.g. ctrl+r for history searching, arrows for moving, etc.).
    // It would be thoughtful if we just disable our yebash if any of these codes would
    // occur and reenable it after a newline
    CharOpt cReturned = c;
    if (handler) {
        handler(c);
    }
    else {
        if (c < 0x20) {
            newlineHandler(c);
        }
        else {
            regularCharHandler(c);
        }
    }

    if (static_cast<bool>(cReturned)) {
        return cReturned.value();
    }

    return c;
}

ssize_t read(int fd, void *buf, size_t count) {

    ssize_t returnValue;
    static thread_local ReadSignature realRead = nullptr;

    if (fd == 0) { // TODO: make it look good
        if (printBuffer.length()) {
            // Return printBuffer to bash one char at time
            *reinterpret_cast<char *>(buf) = *printBufferPos;
            *lineBufferPos++ =  *printBufferPos++;
            if (printBufferPos == printBuffer.end()) {
                printBuffer.erase(printBuffer.begin(), printBuffer.end());
            }
            return 1;
        }
    }

    if (!realRead)
        realRead = reinterpret_cast<ReadSignature>(dlsym(RTLD_NEXT, "read"));

    returnValue = realRead(fd, buf, count);

    if (fd == 0 && isatty(fileno(stdin)))
        *reinterpret_cast<unsigned char *>(buf) = yebash(*reinterpret_cast<unsigned char *>(buf));

    return returnValue;
}

<|endoftext|>


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

  Program:   Insight Segmentation & Registration Toolkit
  Module:    itkBayesianClassifierImageFilterTest.cxx
  Language:  C++
  Date:      $Date$
  Version:   $Revision$

  Copyright (c) Insight Software Consortium. All rights reserved.
  See ITKCopyright.txt or http://www.itk.org/HTML/Copyright.htm for details.

     This software is distributed WITHOUT ANY WARRANTY; without even
     the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
     PURPOSE.  See the above copyright notices for more information.

=========================================================================*/
#if defined(_MSC_VER)
#pragma warning ( disable : 4786 )
#endif

#include "itkImage.h"
#include "itkImageFileReader.h"
#include "itkBayesianClassifierImageFilter.h"
#include "itkBayesianClassifierInitializationImageFilter.h"
#include "itkImageFileWriter.h"
#include "itkGradientAnisotropicDiffusionImageFilter.h"
#include "itkRescaleIntensityImageFilter.h"

#include "../IO/itkPipelineMonitorImageFilter.h"

int itkBayesianClassifierImageFilterTest(int argc, char* argv[] )
{

  if( argc < 5 )
    {
    std::cerr << "Usage: " << std::endl;
    std::cerr << argv[0] << " inputImageFile outputImageFile numberOfClasses smoothingIterations" << std::endl;
    return EXIT_FAILURE;
    }

  // setup reader
  const unsigned int Dimension = 2;
  typedef unsigned char InputPixelType;
  typedef itk::Image< InputPixelType, Dimension > InputImageType;
  typedef itk::ImageFileReader< InputImageType >  ReaderType;

  ReaderType::Pointer reader = ReaderType::New();
  reader->SetFileName( argv[1] );

  typedef unsigned char  LabelType;
  typedef float          PriorType;
  typedef float          PosteriorType;

  typedef itk::BayesianClassifierInitializationImageFilter< InputImageType >
                                                BayesianInitializerType;

  BayesianInitializerType::Pointer bayesianInitializer = BayesianInitializerType::New();

  bayesianInitializer->SetInput( reader->GetOutput() );
  bayesianInitializer->SetNumberOfClasses( atoi( argv[3] ) );

  typedef BayesianInitializerType::OutputImageType  InitialLabelImageType;

  typedef itk::BayesianClassifierImageFilter<
    InitialLabelImageType, LabelType, PosteriorType, PriorType >   ClassifierFilterType;

  ClassifierFilterType::Pointer filter = ClassifierFilterType::New();

  filter->SetInput( bayesianInitializer->GetOutput() );

  //
  //  Exercise Set/GetNumberOfSmoothingIterations()
  //
  filter->SetNumberOfSmoothingIterations( 1 );
  if( filter->GetNumberOfSmoothingIterations() != 1 )
    {
    std::cerr << "Error in Set/GetNumberOfSmoothingIterations()" << std::endl;
    return EXIT_FAILURE;
    }

  filter->SetNumberOfSmoothingIterations( 19 );
  if( filter->GetNumberOfSmoothingIterations() != 19 )
    {
    std::cerr << "Error in Set/GetNumberOfSmoothingIterations()" << std::endl;
    return EXIT_FAILURE;
    }

  filter->SetNumberOfSmoothingIterations( 0 );

  filter->SetNumberOfSmoothingIterations( atoi( argv[4] ));
  typedef ClassifierFilterType::ExtractedComponentImageType ExtractedComponentImageType;
  typedef itk::GradientAnisotropicDiffusionImageFilter<
    ExtractedComponentImageType, ExtractedComponentImageType >  SmoothingFilterType;
  SmoothingFilterType::Pointer smoother = SmoothingFilterType::New();
  smoother->SetNumberOfIterations( 1 );
  smoother->SetTimeStep( 0.125 );
  smoother->SetConductanceParameter( 3 );
  filter->SetSmoothingFilter( smoother );

  //
  //  Exercise Set/GetSmoothingFilter()
  //
  if( filter->GetSmoothingFilter().GetPointer() != smoother.GetPointer() )
    {
    std::cerr << "Error in Set/GetSmoothingFilter()" << std::endl;
    return EXIT_FAILURE;
    }

  filter->SetSmoothingFilter( NULL );
  if( filter->GetSmoothingFilter().GetPointer() != NULL )
    {
    std::cerr << "Error in Set/GetSmoothingFilter()" << std::endl;
    return EXIT_FAILURE;
    }

  filter->SetSmoothingFilter( smoother );

  typedef itk::PipelineMonitorImageFilter<InputImageType> MonitorFilterType;
  MonitorFilterType::Pointer monitor =  MonitorFilterType::New();
  monitor->SetInput(filter->GetOutput());

  typedef ClassifierFilterType::OutputImageType      ClassifierOutputImageType;
  typedef itk::Image< unsigned char, Dimension >     OutputImageType;
  typedef itk::RescaleIntensityImageFilter<
    ClassifierOutputImageType, OutputImageType >   RescalerType;
  RescalerType::Pointer rescaler = RescalerType::New();
  rescaler->SetInput( monitor->GetOutput() );
  rescaler->SetOutputMinimum( 0 );
  rescaler->SetOutputMaximum( 255 );

  typedef itk::ImageFileWriter< OutputImageType >    WriterType;

  WriterType::Pointer writer = WriterType::New();
  writer->SetFileName( argv[2] );

  writer->SetInput( rescaler->GetOutput() );

  try
    {
    filter->Update();
    writer->Update();
    }
  catch( itk::ExceptionObject & excp )
    {
    std::cerr << "Exception caught: " << std::endl;
    std::cerr << excp << std::endl;
    return EXIT_FAILURE;
    }

  if (!monitor->VerifyAllIputCanNotStream())
    {
    std::cout << "pipeline did not execute as expected!" << std::endl;
    return EXIT_FAILURE;
    }

  filter->Print( std::cout );
  std::cout << "Test passed." << std::endl;

  return EXIT_SUCCESS;
}
<commit_msg>ENH: Adding a code for exercising the SetPriors() method.<commit_after>/*=========================================================================

  Program:   Insight Segmentation & Registration Toolkit
  Module:    itkBayesianClassifierImageFilterTest.cxx
  Language:  C++
  Date:      $Date$
  Version:   $Revision$

  Copyright (c) Insight Software Consortium. All rights reserved.
  See ITKCopyright.txt or http://www.itk.org/HTML/Copyright.htm for details.

     This software is distributed WITHOUT ANY WARRANTY; without even
     the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
     PURPOSE.  See the above copyright notices for more information.

=========================================================================*/
#if defined(_MSC_VER)
#pragma warning ( disable : 4786 )
#endif

#include "itkImage.h"
#include "itkImageFileReader.h"
#include "itkBayesianClassifierImageFilter.h"
#include "itkBayesianClassifierInitializationImageFilter.h"
#include "itkImageFileWriter.h"
#include "itkGradientAnisotropicDiffusionImageFilter.h"
#include "itkRescaleIntensityImageFilter.h"

#include "../IO/itkPipelineMonitorImageFilter.h"

int itkBayesianClassifierImageFilterTest(int argc, char* argv[] )
{

  if( argc < 5 )
    {
    std::cerr << "Usage: " << std::endl;
    std::cerr << argv[0] << " inputImageFile outputImageFile numberOfClasses smoothingIterations" << std::endl;
    return EXIT_FAILURE;
    }

  // setup reader
  const unsigned int Dimension = 2;
  typedef unsigned char InputPixelType;
  typedef itk::Image< InputPixelType, Dimension > InputImageType;
  typedef itk::ImageFileReader< InputImageType >  ReaderType;

  ReaderType::Pointer reader = ReaderType::New();
  reader->SetFileName( argv[1] );

  typedef unsigned char  LabelType;
  typedef float          PriorType;
  typedef float          PosteriorType;

  typedef itk::BayesianClassifierInitializationImageFilter< InputImageType >
                                                BayesianInitializerType;

  BayesianInitializerType::Pointer bayesianInitializer = BayesianInitializerType::New();

  bayesianInitializer->SetInput( reader->GetOutput() );
  bayesianInitializer->SetNumberOfClasses( atoi( argv[3] ) );

  typedef BayesianInitializerType::OutputImageType  InitialLabelImageType;

  typedef itk::BayesianClassifierImageFilter<
    InitialLabelImageType, LabelType, PosteriorType, PriorType >   ClassifierFilterType;

  ClassifierFilterType::Pointer filter = ClassifierFilterType::New();

  filter->SetInput( bayesianInitializer->GetOutput() );

  //
  //  Exercise Set/GetNumberOfSmoothingIterations()
  //
  filter->SetNumberOfSmoothingIterations( 1 );
  if( filter->GetNumberOfSmoothingIterations() != 1 )
    {
    std::cerr << "Error in Set/GetNumberOfSmoothingIterations()" << std::endl;
    return EXIT_FAILURE;
    }

  filter->SetNumberOfSmoothingIterations( 19 );
  if( filter->GetNumberOfSmoothingIterations() != 19 )
    {
    std::cerr << "Error in Set/GetNumberOfSmoothingIterations()" << std::endl;
    return EXIT_FAILURE;
    }

  filter->SetNumberOfSmoothingIterations( 0 );

  filter->SetNumberOfSmoothingIterations( atoi( argv[4] ));
  typedef ClassifierFilterType::ExtractedComponentImageType ExtractedComponentImageType;
  typedef itk::GradientAnisotropicDiffusionImageFilter<
    ExtractedComponentImageType, ExtractedComponentImageType >  SmoothingFilterType;
  SmoothingFilterType::Pointer smoother = SmoothingFilterType::New();
  smoother->SetNumberOfIterations( 1 );
  smoother->SetTimeStep( 0.125 );
  smoother->SetConductanceParameter( 3 );
  filter->SetSmoothingFilter( smoother );

  //
  //  Exercise Set/GetSmoothingFilter()
  //
  if( filter->GetSmoothingFilter().GetPointer() != smoother.GetPointer() )
    {
    std::cerr << "Error in Set/GetSmoothingFilter()" << std::endl;
    return EXIT_FAILURE;
    }

  filter->SetSmoothingFilter( NULL );
  if( filter->GetSmoothingFilter().GetPointer() != NULL )
    {
    std::cerr << "Error in Set/GetSmoothingFilter()" << std::endl;
    return EXIT_FAILURE;
    }

  filter->SetSmoothingFilter( smoother );

  typedef itk::PipelineMonitorImageFilter<InputImageType> MonitorFilterType;
  MonitorFilterType::Pointer monitor =  MonitorFilterType::New();
  monitor->SetInput(filter->GetOutput());

  typedef ClassifierFilterType::OutputImageType      ClassifierOutputImageType;
  typedef itk::Image< unsigned char, Dimension >     OutputImageType;
  typedef itk::RescaleIntensityImageFilter<
    ClassifierOutputImageType, OutputImageType >   RescalerType;
  RescalerType::Pointer rescaler = RescalerType::New();
  rescaler->SetInput( monitor->GetOutput() );
  rescaler->SetOutputMinimum( 0 );
  rescaler->SetOutputMaximum( 255 );

  typedef itk::ImageFileWriter< OutputImageType >    WriterType;

  WriterType::Pointer writer = WriterType::New();
  writer->SetFileName( argv[2] );

  writer->SetInput( rescaler->GetOutput() );

  try
    {
    filter->Update();
    writer->Update();
    }
  catch( itk::ExceptionObject & excp )
    {
    std::cerr << "Exception caught: " << std::endl;
    std::cerr << excp << std::endl;
    return EXIT_FAILURE;
    }

  if (!monitor->VerifyAllIputCanNotStream())
    {
    std::cout << "pipeline did not execute as expected!" << std::endl;
    return EXIT_FAILURE;
    }

  filter->Print( std::cout );
  std::cout << "Test passed." << std::endl;

  typedef ClassifierFilterType::PriorsImageType   PriorsImageType;

  const InputImageType * inputImage = reader->GetOutput();

  PriorsImageType::Pointer priorsImage = PriorsImageType::New();
  priorsImage->CopyInformation( inputImage );
  priorsImage->SetRegions( inputImage->GetLargestPossibleRegion() );
  priorsImage->SetNumberOfComponentsPerPixel(5);
  priorsImage->Allocate();

  filter->SetPriors( priorsImage );

  return EXIT_SUCCESS;
}
<|endoftext|>


<commit_before>#pragma once
#ifndef REPLACER_HPP
#define REPLACER_HPP
#include <QString>
#include "tags/textwidget.hpp"
#include "tags/report.hpp"

namespace qtreports
{
    namespace detail
    {

        /*! @~russian
        @brief Класс, заменяющий параметры на реальные значения.

        Класс, заменяющий параметры на реальные значения в тэгах,
        производных от TextWidget
        */
        class Replacer {

        public:
            Replacer();
            ~Replacer();

            /*! @~russian
            Возвращает текст с подставленными значениями параметров.
            @param[in] text Исходный текст
            @param[in] report Указатель на отчет
            */
            QString  replaceParameters( const QString & text, const ReportPtr & report );

            /*! @~russian
            Заменяет текст виджета текстом с подставленными значениями параметров.
            Перегружанная функция, предоставленная для удобства.
            @param[in] widget Указатель на виджет
            @param[in] report Указатель на отчет
            */
            bool     replaceParametersInTextWidget( const TextWidgetPtr & widget, const ReportPtr & report );

            /*! @~russian
            Заменяет текст всех виджетов секции текстом с подставленными значениями параметров.
            Перегружанная функция, предоставленная для удобства.
            @param[in] section Указатель на секцию
            @param[in] report Указатель на отчет
            */
            bool    replaceParametersInSection( const SectionPtr & section, const ReportPtr & report );

            /*! @~russian
            Заменяет текст всех виджетов с текстом в отчете
            текстом с подставленными значениями параметров.
            Перегружанная функция, предоставленная для удобства.
            @param[in] report Указатель на отчет
            */
            bool     replaceParameters( const ReportPtr & report );

            /*! @~russian
            Возвращает текст с подставленными значениями полей из источника данных.
            @param[in] report Указатель на отчет
            @param[in] text Исходный текст
            @param[in] i Текущая строка
            */
            QString  replaceField( const QString & text, const ReportPtr & report, int i );

            /*! @~russian
            Заменяет текст виджета текстом с подставленными значениями полей из источника данных.
            Перегружанная функция, предоставленная для удобства.
            @param[in] widget Указатель на виджет
            @param[in] report Указатель на отчет
            @param[in] i Текущая строка
            */
            bool    replaceFieldInTextWidget( const TextWidgetPtr & widget, const ReportPtr & report, int i );

            /*! @~russian
            Заменяет текст всех виджетов секции
            текстом с подставленными значениями полей из источника данных.
            Перегружанная функция, предоставленная для удобства.
            @param[in] section Указатель на секцию
            @param[in] report Указатель на отчет
            @param[in] i Текущая строка
            */
            bool    replaceFieldInSection( const SectionPtr & section, const ReportPtr & report, int i );

            /*! @~russian
            Заменяет текст всех виджетов секции
            текстом с подставленными значениями.
            Перегружанная функция, предоставленная для удобства.
            @param[in] section Указатель на секцию
            @param[in] report Указатель на отчет
            @param[in] i Текущая строка
            */
            bool    replace( const SectionPtr & section, const ReportPtr & report, int i );

            const QString   getLastError() const;

        private:
            QString     m_lastError;

        };

    }
}

#endif // REPLACER_HPP
<commit_msg>Full doc<commit_after>#pragma once
#ifndef REPLACER_HPP
#define REPLACER_HPP
#include <QString>
#include "tags/textwidget.hpp"
#include "tags/report.hpp"

namespace qtreports
{
    namespace detail
    {

        /*! @~russian
        @brief Класс, заменяющий параметры на реальные значения.

        Класс, заменяющий параметры на реальные значения в тэгах,
        производных от TextWidget
        */
        class Replacer {

        public:
            Replacer();
            ~Replacer();

            /*! @~russian
            Возвращает текст с подставленными значениями параметров.
            @param[in] text Исходный текст
            @param[in] report Указатель на отчет
            */
            QString  replaceParameters( const QString & text, const ReportPtr & report );

            /*! @~russian
            Заменяет текст виджета текстом с подставленными значениями параметров.
            Перегружанная функция, предоставленная для удобства.
            @param[in] widget Указатель на виджет
            @param[in] report Указатель на отчет
            */
            bool     replaceParametersInTextWidget( const TextWidgetPtr & widget, const ReportPtr & report );

            /*! @~russian
            Заменяет текст всех виджетов секции текстом с подставленными значениями параметров.
            Перегружанная функция, предоставленная для удобства.
            @param[in] section Указатель на секцию
            @param[in] report Указатель на отчет
            */
            bool    replaceParametersInSection( const SectionPtr & section, const ReportPtr & report );

            /*! @~russian
            Заменяет текст всех виджетов с текстом в отчете
            текстом с подставленными значениями параметров.
            Перегружанная функция, предоставленная для удобства.
            @param[in] report Указатель на отчет
            */
            bool     replaceParameters( const ReportPtr & report );

            /*! @~russian
            Возвращает текст с подставленными значениями полей из источника данных.
            @param[in] report Указатель на отчет
            @param[in] text Исходный текст
            @param[in] i Текущая строка
            */
            QString  replaceField( const QString & text, const ReportPtr & report, int i );

            /*! @~russian
            Заменяет текст виджета текстом с подставленными значениями полей из источника данных.
            Перегружанная функция, предоставленная для удобства.
            @param[in] widget Указатель на виджет
            @param[in] report Указатель на отчет
            @param[in] i Текущая строка
            */
            bool    replaceFieldInTextWidget( const TextWidgetPtr & widget, const ReportPtr & report, int i );

            /*! @~russian
            Заменяет текст всех виджетов секции
            текстом с подставленными значениями полей из источника данных.
            Перегружанная функция, предоставленная для удобства.
            @param[in] section Указатель на секцию
            @param[in] report Указатель на отчет
            @param[in] i Текущая строка
            */
            bool    replaceFieldInSection( const SectionPtr & section, const ReportPtr & report, int i );

            /*! @~russian
            Заменяет текст всех виджетов секции
            текстом с подставленными значениями.
            Перегружанная функция, предоставленная для удобства.
            @param[in] section Указатель на секцию
            @param[in] report Указатель на отчет
            @param[in] i Текущая строка
            */
            bool    replace( const SectionPtr & section, const ReportPtr & report, int i );

            /*! @~russian
            Возвращает описание последней произошедшей ошибки.
            */
            const QString   getLastError() const;

        private:
            QString     m_lastError;

        };

    }
}

#endif // REPLACER_HPP
<|endoftext|>


<commit_before>//
// WinAPI
//
#define WIN32_LEAN_AND_MEAN
#define NOMINMAX
#include <Windows.h>
#include <SetupAPI.h>
#include <Shlwapi.h>
#include <initguid.h>
#include <winioctl.h>
#include "XUSB.h"

//
// STL
//
#include <string>
#include <codecvt>
#include <locale>
#include <map>
#include <iostream>
#include <fstream>

//
// JSON
//
#include <json/json.h>

//
// Hooking
//
#include <detours/detours.h>

//
// Logging
//
#include <spdlog/spdlog.h>
#include <spdlog/sinks/basic_file_sink.h>
#include <spdlog/fmt/bin_to_hex.h>

using convert_t = std::codecvt_utf8<wchar_t>;
std::wstring_convert<convert_t, wchar_t> strconverter;
std::once_flag g_init;
std::string g_dllDir;

static decltype(SetupDiEnumDeviceInterfaces) *real_SetupDiEnumDeviceInterfaces = SetupDiEnumDeviceInterfaces;
static decltype(DeviceIoControl) *real_DeviceIoControl = DeviceIoControl;
static decltype(CreateFileA) *real_CreateFileA = CreateFileA;
static decltype(CreateFileW) *real_CreateFileW = CreateFileW;
static decltype(WriteFile)* real_WriteFile = WriteFile;
static decltype(CloseHandle)* real_CloseHandle = CloseHandle;
static decltype(GetOverlappedResult)* real_GetOverlappedResult = GetOverlappedResult;

static std::map<HANDLE, std::string> g_handleToPath;
static std::map<DWORD, std::string> g_ioctlMap;

//
// Hooks SetupDiEnumDeviceInterfaces() API
//
BOOL WINAPI DetourSetupDiEnumDeviceInterfaces(
	HDEVINFO DeviceInfoSet,
	PSP_DEVINFO_DATA DeviceInfoData,
	const GUID* InterfaceClassGuid,
	DWORD MemberIndex,
	PSP_DEVICE_INTERFACE_DATA DeviceInterfaceData
)
{
	std::shared_ptr<spdlog::logger> _logger = spdlog::get("XInputHooker")->clone("SetupDiEnumDeviceInterfaces");

	auto retval = real_SetupDiEnumDeviceInterfaces(DeviceInfoSet, DeviceInfoData, InterfaceClassGuid, MemberIndex,
	                                               DeviceInterfaceData);

	_logger->info("InterfaceClassGuid = {{{:08X}-{:04X}-{:04X}-{:02X}{:02X}-{:02X}{:02X}{:02X}{:02X}{:02X}{:02X}}}, "
	              "success = {}, error = 0x{:08X}",
	              InterfaceClassGuid->Data1, InterfaceClassGuid->Data2, InterfaceClassGuid->Data3,
	              InterfaceClassGuid->Data4[0], InterfaceClassGuid->Data4[1], InterfaceClassGuid->Data4[2],
	              InterfaceClassGuid->Data4[3],
	              InterfaceClassGuid->Data4[4], InterfaceClassGuid->Data4[5], InterfaceClassGuid->Data4[6],
	              InterfaceClassGuid->Data4[7],
	              retval ? "true" : "false",
				  retval ? ERROR_SUCCESS : GetLastError());

	return retval;
}

//
// Hooks CreateFileA() API
//
HANDLE WINAPI DetourCreateFileA(
	LPCSTR lpFileName,
	DWORD dwDesiredAccess,
	DWORD dwShareMode,
	LPSECURITY_ATTRIBUTES lpSecurityAttributes,
	DWORD dwCreationDisposition,
	DWORD dwFlagsAndAttributes,
	HANDLE hTemplateFile
)
{
	std::shared_ptr<spdlog::logger> _logger = spdlog::get("XInputHooker")->clone("CreateFileA");
	std::string path(lpFileName);

	const bool isOfInterest = (path.rfind("\\\\", 0) == 0);

	const auto handle = real_CreateFileA(
		lpFileName,
		dwDesiredAccess,
		dwShareMode,
		lpSecurityAttributes,
		dwCreationDisposition,
		dwFlagsAndAttributes,
		hTemplateFile
	);

	if (isOfInterest && handle != INVALID_HANDLE_VALUE)
	{
		g_handleToPath[handle] = path;
		_logger->info("handle = {}, lpFileName = {}", handle, path);
	}

	return handle;
}

//
// Hooks CreateFileW() API
//
HANDLE WINAPI DetourCreateFileW(
	LPCWSTR lpFileName,
	DWORD dwDesiredAccess,
	DWORD dwShareMode,
	LPSECURITY_ATTRIBUTES lpSecurityAttributes,
	DWORD dwCreationDisposition,
	DWORD dwFlagsAndAttributes,
	HANDLE hTemplateFile
)
{
	std::shared_ptr<spdlog::logger> _logger = spdlog::get("XInputHooker")->clone("CreateFileW");
	std::string path(strconverter.to_bytes(lpFileName));

	const bool isOfInterest = (path.rfind("\\\\", 0) == 0);

	const auto handle = real_CreateFileW(
		lpFileName,
		dwDesiredAccess,
		dwShareMode,
		lpSecurityAttributes,
		dwCreationDisposition,
		dwFlagsAndAttributes,
		hTemplateFile
	);

	if (isOfInterest && handle != INVALID_HANDLE_VALUE)
	{
		g_handleToPath[handle] = path;
		_logger->info("handle = {}, lpFileName = {}", handle, path);
	}

	return handle;
}

//
// Hooks WriteFile() API
//
BOOL WINAPI DetourWriteFile(
	HANDLE       hFile,
	LPCVOID      lpBuffer,
	DWORD        nNumberOfBytesToWrite,
	LPDWORD      lpNumberOfBytesWritten,
	LPOVERLAPPED lpOverlapped
)
{
	std::shared_ptr<spdlog::logger> _logger = spdlog::get("XInputHooker")->clone("WriteFile");

	const PUCHAR charInBuf = PUCHAR(lpBuffer);
	DWORD tmpBytesWritten;

	const auto ret =  real_WriteFile(hFile, lpBuffer, nNumberOfBytesToWrite, &tmpBytesWritten, lpOverlapped);
	const auto error = GetLastError();

	if (lpNumberOfBytesWritten)
		*lpNumberOfBytesWritten = tmpBytesWritten;

	std::string path = "Unknown";
	if (g_handleToPath.count(hFile))
	{
		path = g_handleToPath[hFile];
	}
#ifndef XINPUTHOOKER_LOG_UNKNOWN_HANDLES
	else
	{
		// Ignore unknown handles
		return ret;
	}
#endif

	const auto bufSize = std::min(nNumberOfBytesToWrite, tmpBytesWritten);
	const std::vector<char> inBuffer(charInBuf, charInBuf + bufSize);

	// Prevent the logger from causing a crash via exception when it double-detours WriteFile
	try
	{
		_logger->info("success = {}, lastError = 0x{:08X}, path = {} ({:04d}) -> {:Xpn}",
			ret ? "true" : "false",
			ret ? ERROR_SUCCESS : error,
			path,
			bufSize,
			spdlog::to_hex(inBuffer)
		);
	}
	catch (...)
	{ }

	return ret;
}

BOOL DetourCloseHandle(
	HANDLE hObject
)
{
	std::shared_ptr<spdlog::logger> _logger = spdlog::get("XInputHooker")->clone("CloseHandle");

	const auto ret = real_CloseHandle(hObject);

	std::string path = "Unknown";
	if (g_handleToPath.count(hObject))
	{
		path = g_handleToPath[hObject];
		g_handleToPath.erase(hObject);
	}
#ifndef XINPUTHOOKER_LOG_UNKNOWN_HANDLES
	else
	{
		// Ignore unknown handles
		return ret;
	}
#endif

	_logger->info("handle = {}, path = {}", hObject, path);

	return ret;
}

BOOL WINAPI DetourGetOverlappedResult(
	HANDLE       hFile,
	LPOVERLAPPED lpOverlapped,
	LPDWORD      lpNumberOfBytesTransferred,
	BOOL         bWait
)
{
	std::shared_ptr<spdlog::logger> _logger = spdlog::get("XInputHooker")->clone("GetOverlappedResult");
	DWORD tmpBytesTransferred;

	const auto ret = real_GetOverlappedResult(hFile, lpOverlapped, &tmpBytesTransferred, bWait);
	const auto error = GetLastError();

	if (lpNumberOfBytesTransferred)
		*lpNumberOfBytesTransferred = tmpBytesTransferred;

	std::string path = "Unknown";
	if (g_handleToPath.count(hFile))
	{
		path = g_handleToPath[hFile];
	}
#ifndef XINPUTHOOKER_LOG_UNKNOWN_HANDLES
	else
	{
		// Ignore unknown handles
		return ret;
	}
#endif

	_logger->info("success = {}, lastError = 0x{:08X}, bytesTransferred = {}, path = {}",
		ret ? "true" : "false",
		ret ? ERROR_SUCCESS : error,
		tmpBytesTransferred,
		path
	);

	return ret;
}

//
// Hooks DeviceIoControl() API
//
BOOL WINAPI DetourDeviceIoControl(
	HANDLE hDevice,
	DWORD dwIoControlCode,
	LPVOID lpInBuffer,
	DWORD nInBufferSize,
	LPVOID lpOutBuffer,
	DWORD nOutBufferSize,
	LPDWORD lpBytesReturned,
	LPOVERLAPPED lpOverlapped
)
{
	std::shared_ptr<spdlog::logger> _logger = spdlog::get("XInputHooker")->clone("DeviceIoControl");

	const PUCHAR charInBuf = static_cast<PUCHAR>(lpInBuffer);
	const std::vector<char> inBuffer(charInBuf, charInBuf + nInBufferSize);

	DWORD tmpBytesReturned;

	const auto retval = real_DeviceIoControl(
		hDevice,
		dwIoControlCode,
		lpInBuffer,
		nInBufferSize,
		lpOutBuffer,
		nOutBufferSize,
		&tmpBytesReturned, // might be null, use our own variable
		lpOverlapped
	);

	if (lpBytesReturned)
		*lpBytesReturned = tmpBytesReturned;

	std::string path = "Unknown";
	if (g_handleToPath.count(hDevice))
	{
		path = g_handleToPath[hDevice];
	}
#ifndef XINPUTHOOKER_LOG_UNKNOWN_HANDLES
	else
	{
		// Ignore unknown handles
		return retval;
	}
#endif

	if (g_ioctlMap.count(dwIoControlCode))
	{
		_logger->info("[I] [{}] path = {} ({:04d}) -> {:Xpn}",
		              g_ioctlMap[dwIoControlCode],
		              path,
		              nInBufferSize,
		              spdlog::to_hex(inBuffer)
		);
	}

	if (lpOutBuffer && nOutBufferSize > 0)
	{
		const PUCHAR charOutBuf = static_cast<PUCHAR>(lpOutBuffer);
		const auto bufSize = std::min(nOutBufferSize, tmpBytesReturned);
		const std::vector<char> outBuffer(charOutBuf, charOutBuf + bufSize);

		if (g_ioctlMap.count(dwIoControlCode))
		{
			_logger->info("[O] [{}] path = {} ({:04d}) -> {:Xpn}",
			              g_ioctlMap[dwIoControlCode],
			              path,
			              bufSize,
			              spdlog::to_hex(outBuffer)
			);
		}
	}

	return retval;
}

EXTERN_C IMAGE_DOS_HEADER __ImageBase;

BOOL WINAPI DllMain(HINSTANCE dll_handle, DWORD reason, LPVOID reserved)
{
	if (DetourIsHelperProcess())
	{
		return TRUE;
	}

	switch (reason)
	{
	case DLL_PROCESS_ATTACH:
		{
			CHAR dllPath[MAX_PATH];

			GetModuleFileNameA((HINSTANCE)&__ImageBase, dllPath, MAX_PATH);
			PathRemoveFileSpecA(dllPath);
			g_dllDir = std::string(dllPath);

			auto logger = spdlog::basic_logger_mt(
				"XInputHooker",
				g_dllDir + "\\XInputHooker.log"
			);

#if _DEBUG
			spdlog::set_level(spdlog::level::debug);
			logger->flush_on(spdlog::level::debug);
#else
			logger->flush_on(spdlog::level::info);
#endif

			set_default_logger(logger);

			//
			// Load known IOCTL code definitions
			//
			Json::Value root;
			std::ifstream ifs(g_dllDir + "\\ioctls.json");
			ifs >> root;

			for (auto& i : root)
			{
				g_ioctlMap[std::stoul(i["HexValue"].asString(), nullptr, 16)] = i["Ioctl"].asString();
			}
		}

		DisableThreadLibraryCalls(dll_handle);
		DetourRestoreAfterWith();

		DetourTransactionBegin();
		DetourUpdateThread(GetCurrentThread());
		DetourAttach((PVOID*)&real_SetupDiEnumDeviceInterfaces, DetourSetupDiEnumDeviceInterfaces);
		DetourAttach((PVOID*)&real_DeviceIoControl, DetourDeviceIoControl);
		DetourAttach((PVOID*)&real_CreateFileA, DetourCreateFileA);
		DetourAttach((PVOID*)&real_CreateFileW, DetourCreateFileW);
		DetourAttach((PVOID*)&real_WriteFile, DetourWriteFile);
		DetourAttach((PVOID*)&real_CloseHandle, DetourCloseHandle);
		DetourAttach((PVOID*)&real_GetOverlappedResult, DetourGetOverlappedResult);
		DetourTransactionCommit();

		break;

	case DLL_PROCESS_DETACH:
		DetourTransactionBegin();
		DetourUpdateThread(GetCurrentThread());
		DetourDetach((PVOID*)&real_SetupDiEnumDeviceInterfaces, DetourSetupDiEnumDeviceInterfaces);
		DetourDetach((PVOID*)&real_DeviceIoControl, DetourDeviceIoControl);
		DetourDetach((PVOID*)&real_CreateFileA, DetourCreateFileA);
		DetourDetach((PVOID*)&real_CreateFileW, DetourCreateFileW);
		DetourDetach((PVOID*)&real_WriteFile, DetourWriteFile);
		DetourDetach((PVOID*)&real_CloseHandle, DetourCloseHandle);
		DetourDetach((PVOID*)&real_GetOverlappedResult, DetourGetOverlappedResult);
		DetourTransactionCommit();
		break;
	}
	return TRUE;
}
<commit_msg>Log path and error code if CreateFile fails<commit_after>//
// WinAPI
//
#define WIN32_LEAN_AND_MEAN
#define NOMINMAX
#include <Windows.h>
#include <SetupAPI.h>
#include <Shlwapi.h>
#include <initguid.h>
#include <winioctl.h>
#include "XUSB.h"

//
// STL
//
#include <string>
#include <codecvt>
#include <locale>
#include <map>
#include <iostream>
#include <fstream>

//
// JSON
//
#include <json/json.h>

//
// Hooking
//
#include <detours/detours.h>

//
// Logging
//
#include <spdlog/spdlog.h>
#include <spdlog/sinks/basic_file_sink.h>
#include <spdlog/fmt/bin_to_hex.h>

using convert_t = std::codecvt_utf8<wchar_t>;
std::wstring_convert<convert_t, wchar_t> strconverter;
std::once_flag g_init;
std::string g_dllDir;

static decltype(SetupDiEnumDeviceInterfaces) *real_SetupDiEnumDeviceInterfaces = SetupDiEnumDeviceInterfaces;
static decltype(DeviceIoControl) *real_DeviceIoControl = DeviceIoControl;
static decltype(CreateFileA) *real_CreateFileA = CreateFileA;
static decltype(CreateFileW) *real_CreateFileW = CreateFileW;
static decltype(WriteFile)* real_WriteFile = WriteFile;
static decltype(CloseHandle)* real_CloseHandle = CloseHandle;
static decltype(GetOverlappedResult)* real_GetOverlappedResult = GetOverlappedResult;

static std::map<HANDLE, std::string> g_handleToPath;
static std::map<DWORD, std::string> g_ioctlMap;

//
// Hooks SetupDiEnumDeviceInterfaces() API
//
BOOL WINAPI DetourSetupDiEnumDeviceInterfaces(
	HDEVINFO DeviceInfoSet,
	PSP_DEVINFO_DATA DeviceInfoData,
	const GUID* InterfaceClassGuid,
	DWORD MemberIndex,
	PSP_DEVICE_INTERFACE_DATA DeviceInterfaceData
)
{
	std::shared_ptr<spdlog::logger> _logger = spdlog::get("XInputHooker")->clone("SetupDiEnumDeviceInterfaces");

	auto retval = real_SetupDiEnumDeviceInterfaces(DeviceInfoSet, DeviceInfoData, InterfaceClassGuid, MemberIndex,
	                                               DeviceInterfaceData);

	_logger->info("InterfaceClassGuid = {{{:08X}-{:04X}-{:04X}-{:02X}{:02X}-{:02X}{:02X}{:02X}{:02X}{:02X}{:02X}}}, "
	              "success = {}, error = 0x{:08X}",
	              InterfaceClassGuid->Data1, InterfaceClassGuid->Data2, InterfaceClassGuid->Data3,
	              InterfaceClassGuid->Data4[0], InterfaceClassGuid->Data4[1], InterfaceClassGuid->Data4[2],
	              InterfaceClassGuid->Data4[3],
	              InterfaceClassGuid->Data4[4], InterfaceClassGuid->Data4[5], InterfaceClassGuid->Data4[6],
	              InterfaceClassGuid->Data4[7],
	              retval ? "true" : "false",
				  retval ? ERROR_SUCCESS : GetLastError());

	return retval;
}

//
// Hooks CreateFileA() API
//
HANDLE WINAPI DetourCreateFileA(
	LPCSTR lpFileName,
	DWORD dwDesiredAccess,
	DWORD dwShareMode,
	LPSECURITY_ATTRIBUTES lpSecurityAttributes,
	DWORD dwCreationDisposition,
	DWORD dwFlagsAndAttributes,
	HANDLE hTemplateFile
)
{
	std::shared_ptr<spdlog::logger> _logger = spdlog::get("XInputHooker")->clone("CreateFileA");
	std::string path(lpFileName);

	const bool isOfInterest = (path.rfind("\\\\", 0) == 0);

	const auto handle = real_CreateFileA(
		lpFileName,
		dwDesiredAccess,
		dwShareMode,
		lpSecurityAttributes,
		dwCreationDisposition,
		dwFlagsAndAttributes,
		hTemplateFile
	);

	if (isOfInterest)
	{
		if (handle != INVALID_HANDLE_VALUE)
		{
			g_handleToPath[handle] = path;
			_logger->info("handle = {}, lpFileName = {}", handle, path);
		}
		else
		{
			_logger->info("lpFileName = {}, lastError = {}", path, GetLastError());
		}
	}

	return handle;
}

//
// Hooks CreateFileW() API
//
HANDLE WINAPI DetourCreateFileW(
	LPCWSTR lpFileName,
	DWORD dwDesiredAccess,
	DWORD dwShareMode,
	LPSECURITY_ATTRIBUTES lpSecurityAttributes,
	DWORD dwCreationDisposition,
	DWORD dwFlagsAndAttributes,
	HANDLE hTemplateFile
)
{
	std::shared_ptr<spdlog::logger> _logger = spdlog::get("XInputHooker")->clone("CreateFileW");
	std::string path(strconverter.to_bytes(lpFileName));

	const bool isOfInterest = (path.rfind("\\\\", 0) == 0);

	const auto handle = real_CreateFileW(
		lpFileName,
		dwDesiredAccess,
		dwShareMode,
		lpSecurityAttributes,
		dwCreationDisposition,
		dwFlagsAndAttributes,
		hTemplateFile
	);

	if (isOfInterest)
	{
		if (handle != INVALID_HANDLE_VALUE)
		{
			g_handleToPath[handle] = path;
			_logger->info("handle = {}, lpFileName = {}", handle, path);
		}
		else
		{
			_logger->info("lpFileName = {}, lastError = {}", path, GetLastError());
		}
	}

	return handle;
}

//
// Hooks WriteFile() API
//
BOOL WINAPI DetourWriteFile(
	HANDLE       hFile,
	LPCVOID      lpBuffer,
	DWORD        nNumberOfBytesToWrite,
	LPDWORD      lpNumberOfBytesWritten,
	LPOVERLAPPED lpOverlapped
)
{
	std::shared_ptr<spdlog::logger> _logger = spdlog::get("XInputHooker")->clone("WriteFile");

	const PUCHAR charInBuf = PUCHAR(lpBuffer);
	DWORD tmpBytesWritten;

	const auto ret =  real_WriteFile(hFile, lpBuffer, nNumberOfBytesToWrite, &tmpBytesWritten, lpOverlapped);
	const auto error = GetLastError();

	if (lpNumberOfBytesWritten)
		*lpNumberOfBytesWritten = tmpBytesWritten;

	std::string path = "Unknown";
	if (g_handleToPath.count(hFile))
	{
		path = g_handleToPath[hFile];
	}
#ifndef XINPUTHOOKER_LOG_UNKNOWN_HANDLES
	else
	{
		// Ignore unknown handles
		return ret;
	}
#endif

	const auto bufSize = std::min(nNumberOfBytesToWrite, tmpBytesWritten);
	const std::vector<char> inBuffer(charInBuf, charInBuf + bufSize);

	// Prevent the logger from causing a crash via exception when it double-detours WriteFile
	try
	{
		_logger->info("success = {}, lastError = 0x{:08X}, path = {} ({:04d}) -> {:Xpn}",
			ret ? "true" : "false",
			ret ? ERROR_SUCCESS : error,
			path,
			bufSize,
			spdlog::to_hex(inBuffer)
		);
	}
	catch (...)
	{ }

	return ret;
}

BOOL DetourCloseHandle(
	HANDLE hObject
)
{
	std::shared_ptr<spdlog::logger> _logger = spdlog::get("XInputHooker")->clone("CloseHandle");

	const auto ret = real_CloseHandle(hObject);

	std::string path = "Unknown";
	if (g_handleToPath.count(hObject))
	{
		path = g_handleToPath[hObject];
		g_handleToPath.erase(hObject);
	}
#ifndef XINPUTHOOKER_LOG_UNKNOWN_HANDLES
	else
	{
		// Ignore unknown handles
		return ret;
	}
#endif

	_logger->info("handle = {}, path = {}", hObject, path);

	return ret;
}

BOOL WINAPI DetourGetOverlappedResult(
	HANDLE       hFile,
	LPOVERLAPPED lpOverlapped,
	LPDWORD      lpNumberOfBytesTransferred,
	BOOL         bWait
)
{
	std::shared_ptr<spdlog::logger> _logger = spdlog::get("XInputHooker")->clone("GetOverlappedResult");
	DWORD tmpBytesTransferred;

	const auto ret = real_GetOverlappedResult(hFile, lpOverlapped, &tmpBytesTransferred, bWait);
	const auto error = GetLastError();

	if (lpNumberOfBytesTransferred)
		*lpNumberOfBytesTransferred = tmpBytesTransferred;

	std::string path = "Unknown";
	if (g_handleToPath.count(hFile))
	{
		path = g_handleToPath[hFile];
	}
#ifndef XINPUTHOOKER_LOG_UNKNOWN_HANDLES
	else
	{
		// Ignore unknown handles
		return ret;
	}
#endif

	_logger->info("success = {}, lastError = 0x{:08X}, bytesTransferred = {}, path = {}",
		ret ? "true" : "false",
		ret ? ERROR_SUCCESS : error,
		tmpBytesTransferred,
		path
	);

	return ret;
}

//
// Hooks DeviceIoControl() API
//
BOOL WINAPI DetourDeviceIoControl(
	HANDLE hDevice,
	DWORD dwIoControlCode,
	LPVOID lpInBuffer,
	DWORD nInBufferSize,
	LPVOID lpOutBuffer,
	DWORD nOutBufferSize,
	LPDWORD lpBytesReturned,
	LPOVERLAPPED lpOverlapped
)
{
	std::shared_ptr<spdlog::logger> _logger = spdlog::get("XInputHooker")->clone("DeviceIoControl");

	const PUCHAR charInBuf = static_cast<PUCHAR>(lpInBuffer);
	const std::vector<char> inBuffer(charInBuf, charInBuf + nInBufferSize);

	DWORD tmpBytesReturned;

	const auto retval = real_DeviceIoControl(
		hDevice,
		dwIoControlCode,
		lpInBuffer,
		nInBufferSize,
		lpOutBuffer,
		nOutBufferSize,
		&tmpBytesReturned, // might be null, use our own variable
		lpOverlapped
	);

	if (lpBytesReturned)
		*lpBytesReturned = tmpBytesReturned;

	std::string path = "Unknown";
	if (g_handleToPath.count(hDevice))
	{
		path = g_handleToPath[hDevice];
	}
#ifndef XINPUTHOOKER_LOG_UNKNOWN_HANDLES
	else
	{
		// Ignore unknown handles
		return retval;
	}
#endif

	if (g_ioctlMap.count(dwIoControlCode))
	{
		_logger->info("[I] [{}] path = {} ({:04d}) -> {:Xpn}",
		              g_ioctlMap[dwIoControlCode],
		              path,
		              nInBufferSize,
		              spdlog::to_hex(inBuffer)
		);
	}

	if (lpOutBuffer && nOutBufferSize > 0)
	{
		const PUCHAR charOutBuf = static_cast<PUCHAR>(lpOutBuffer);
		const auto bufSize = std::min(nOutBufferSize, tmpBytesReturned);
		const std::vector<char> outBuffer(charOutBuf, charOutBuf + bufSize);

		if (g_ioctlMap.count(dwIoControlCode))
		{
			_logger->info("[O] [{}] path = {} ({:04d}) -> {:Xpn}",
			              g_ioctlMap[dwIoControlCode],
			              path,
			              bufSize,
			              spdlog::to_hex(outBuffer)
			);
		}
	}

	return retval;
}

EXTERN_C IMAGE_DOS_HEADER __ImageBase;

BOOL WINAPI DllMain(HINSTANCE dll_handle, DWORD reason, LPVOID reserved)
{
	if (DetourIsHelperProcess())
	{
		return TRUE;
	}

	switch (reason)
	{
	case DLL_PROCESS_ATTACH:
		{
			CHAR dllPath[MAX_PATH];

			GetModuleFileNameA((HINSTANCE)&__ImageBase, dllPath, MAX_PATH);
			PathRemoveFileSpecA(dllPath);
			g_dllDir = std::string(dllPath);

			auto logger = spdlog::basic_logger_mt(
				"XInputHooker",
				g_dllDir + "\\XInputHooker.log"
			);

#if _DEBUG
			spdlog::set_level(spdlog::level::debug);
			logger->flush_on(spdlog::level::debug);
#else
			logger->flush_on(spdlog::level::info);
#endif

			set_default_logger(logger);

			//
			// Load known IOCTL code definitions
			//
			Json::Value root;
			std::ifstream ifs(g_dllDir + "\\ioctls.json");
			ifs >> root;

			for (auto& i : root)
			{
				g_ioctlMap[std::stoul(i["HexValue"].asString(), nullptr, 16)] = i["Ioctl"].asString();
			}
		}

		DisableThreadLibraryCalls(dll_handle);
		DetourRestoreAfterWith();

		DetourTransactionBegin();
		DetourUpdateThread(GetCurrentThread());
		DetourAttach((PVOID*)&real_SetupDiEnumDeviceInterfaces, DetourSetupDiEnumDeviceInterfaces);
		DetourAttach((PVOID*)&real_DeviceIoControl, DetourDeviceIoControl);
		DetourAttach((PVOID*)&real_CreateFileA, DetourCreateFileA);
		DetourAttach((PVOID*)&real_CreateFileW, DetourCreateFileW);
		DetourAttach((PVOID*)&real_WriteFile, DetourWriteFile);
		DetourAttach((PVOID*)&real_CloseHandle, DetourCloseHandle);
		DetourAttach((PVOID*)&real_GetOverlappedResult, DetourGetOverlappedResult);
		DetourTransactionCommit();

		break;

	case DLL_PROCESS_DETACH:
		DetourTransactionBegin();
		DetourUpdateThread(GetCurrentThread());
		DetourDetach((PVOID*)&real_SetupDiEnumDeviceInterfaces, DetourSetupDiEnumDeviceInterfaces);
		DetourDetach((PVOID*)&real_DeviceIoControl, DetourDeviceIoControl);
		DetourDetach((PVOID*)&real_CreateFileA, DetourCreateFileA);
		DetourDetach((PVOID*)&real_CreateFileW, DetourCreateFileW);
		DetourDetach((PVOID*)&real_WriteFile, DetourWriteFile);
		DetourDetach((PVOID*)&real_CloseHandle, DetourCloseHandle);
		DetourDetach((PVOID*)&real_GetOverlappedResult, DetourGetOverlappedResult);
		DetourTransactionCommit();
		break;
	}
	return TRUE;
}
<|endoftext|>


<commit_before>#include "group_shared.hpp"
#include <cassert>

#include <fcntl.h>
#include <sys/stat.h>
#include <sys/mman.h>

using namespace tightdb;

// Pre-declare local functions
char* concat_strings(const char* str1, const char* str2);

struct tightdb::ReadCount {
    uint32_t version;
    uint32_t count;
};

struct tightdb::SharedInfo {
    pthread_mutex_t readmutex;
    pthread_mutex_t writemutex;
    uint64_t filesize;
    uint32_t infosize;

    uint64_t current_top;
    uint32_t current_version;

    uint32_t capacity; // -1 so it can also be used as mask
    uint32_t put_pos;
    uint32_t get_pos;
    ReadCount readers[32]; // has to be power of two
};

// Does not work for windows yet
#ifndef _MSC_VER

SharedGroup::SharedGroup(const char* filename) : m_group(filename, false), m_info(NULL), m_isValid(false), m_version(-1)
{
    if (!m_group.is_valid()) return;

    // Open shared coordination buffer
    const char* const path = concat_strings(filename, ".lock");
    const int fd = open(path, O_RDWR|O_CREAT, S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH);
    free((void*)path);
    if (fd < 0) return;

    // Get size
    struct stat statbuf;
    if (fstat(fd, &statbuf) < 0) {
        close(fd);
        return;
    }
    size_t len = statbuf.st_size;

    // Handle empty files (first user)
    bool needInit = false;
    if (len == 0) {
        // Create new file
        len = sizeof(SharedInfo);
        ftruncate(fd, len);
        needInit = true;
    }

    // Map to memory
    void* const p = mmap(0, len, PROT_READ|PROT_WRITE, MAP_SHARED, fd, 0);
    close(fd); // no loner need file discriptor
    if (p == (void*)-1) return;

    m_info = (SharedInfo*)p;

    if (needInit) {
        pthread_mutex_lock(&m_info->readmutex);
        {
            const SlabAlloc& alloc = m_group.get_allocator();

            m_info->filesize = alloc.GetFileLen();
            m_info->infosize = (uint32_t)len;
            m_info->current_top = alloc.GetTopRef();;
            m_info->current_version = 0;
            m_info->capacity = 32-1;
            m_info->put_pos = 0;
            m_info->get_pos = 0;
        }
        pthread_mutex_unlock(&m_info->readmutex);
    }

    m_isValid = true;
}

SharedGroup::~SharedGroup()
{
    if (m_info) {
        munmap((void*)m_info, m_info->infosize);
    }
}

const Group& SharedGroup::start_read()
{
    size_t new_topref = 0;
    size_t new_filesize = 0;

    pthread_mutex_lock(&m_info->readmutex);
    {
        // Get the current top ref
        new_topref   = m_info->current_top;
        new_filesize = m_info->filesize;
        m_version    = m_info->current_version;

        // Update reader list
        if (ringbuf_is_empty()) {
            const ReadCount r2 = {m_info->current_version, 1};
            ringbuf_put(r2);
        }
        else {
            ReadCount& r = ringbuf_get_last();
            if (r.version == m_info->current_version)
                ++(r.count);
            else {
                const ReadCount r2 = {m_info->current_version, 1};
                ringbuf_put(r2);
            }
        }
    }
    pthread_mutex_unlock(&m_info->readmutex);

    // Make sure the group is up-to-date
    m_group.update_from_shared(new_topref, new_filesize);

    return m_group;
}

void SharedGroup::end_read()
{
    assert(m_version != (uint32_t)-1);

    pthread_mutex_lock(&m_info->readmutex);
    {
        // Find entry for current version
        const size_t ndx = ringbuf_find(m_version);
        assert(ndx != (size_t)-1);
        ReadCount& r = ringbuf_get(ndx);

        // Decrement count and remove as many entries as possible
        if (r.count == 1 && ringbuf_is_first(ndx)) {
            ringbuf_remove_first();
            while (!ringbuf_is_empty() && ringbuf_get_first().count == 0) {
                ringbuf_remove_first();
            }
        }
        else {
            assert(r.count > 0);
            --r.count;
        }
    }
    pthread_mutex_unlock(&m_info->readmutex);

    m_version = (uint32_t)-1;
}

Group& SharedGroup::start_write()
{
    // Get write lock
    // Note that this will not get released until we call
    // end_write().
    pthread_mutex_lock(&m_info->writemutex);

    // Get the current top ref
    const size_t new_topref   = m_info->current_top;
    const size_t new_filesize = m_info->filesize;

    // Make sure the group is up-to-date
    // zero ref means that the file has just been created
    if (new_topref != 0) {
        m_group.update_from_shared(new_topref, new_filesize);
    }

    return m_group;
}

void SharedGroup::end_write()
{
    m_group.commit();

    // Get the current top ref
    const SlabAlloc& alloc = m_group.get_allocator();
    const size_t new_topref   = alloc.GetTopRef();
    const size_t new_filesize = alloc.GetFileLen();

    // Update reader info
    pthread_mutex_lock(&m_info->readmutex);
    {
        m_info->current_top = new_topref;
        m_info->filesize    = new_filesize;
        ++m_info->current_version;
    }
    pthread_mutex_unlock(&m_info->readmutex);

    // Release write lock
    pthread_mutex_unlock(&m_info->writemutex);
}

bool SharedGroup::ringbuf_is_empty() const
{
    return (ringbuf_size() == 0);
}

size_t SharedGroup::ringbuf_size() const
{
    return ((m_info->put_pos - m_info->get_pos) & m_info->capacity);
}

size_t SharedGroup::ringbuf_capacity() const
{
    return m_info->capacity+1;
}

bool SharedGroup::ringbuf_is_first(size_t ndx) const {
    return (ndx == m_info->get_pos);
}

ReadCount& SharedGroup::ringbuf_get(size_t ndx)
{
    return m_info->readers[ndx];
}

ReadCount& SharedGroup::ringbuf_get_first()
{
    return m_info->readers[m_info->get_pos];
}

ReadCount& SharedGroup::ringbuf_get_last()
{
    const uint32_t lastPos = (m_info->put_pos - 1) & m_info->capacity;
    return m_info->readers[lastPos];
}

void SharedGroup::ringbuf_remove_first() {
    m_info->get_pos = (m_info->get_pos + 1) & m_info->capacity;
}

void SharedGroup::ringbuf_put(const ReadCount& v)
{
    const bool isFull = (ringbuf_size() == (m_info->capacity+1));

    if(isFull) {
        //TODO: expand buffer
        assert(false);
    }

    m_info->readers[m_info->put_pos] = v;
    m_info->put_pos = (m_info->put_pos + 1) & m_info->capacity;
}

size_t SharedGroup::ringbuf_find(uint32_t version) const
{
    uint32_t pos = m_info->get_pos;
    while (pos != m_info->put_pos) {
        const ReadCount& r = m_info->readers[pos];
        if (r.version == version)
            return pos;

        pos = (pos + 1) & m_info->capacity;
    }

    return (size_t)-1;
}

void SharedGroup::test_ringbuf()
{
    assert(ringbuf_is_empty());

    const ReadCount rc = {1, 1};
    ringbuf_put(rc);
    assert(ringbuf_size() == 1);

    ringbuf_remove_first();
    assert(ringbuf_is_empty());

    // Fill buffer
    const size_t capacity = ringbuf_capacity();
    for (size_t i = 0; i < capacity; ++i) {
        const ReadCount r = {1, (uint32_t)i};
        ringbuf_put(r);
        assert(ringbuf_get_last().count == i);
    }
    for (size_t i = 0; i < 32; ++i) {
        const ReadCount& r = ringbuf_get_first();
        assert(r.count == i);
        ringbuf_remove_first();
    }
    assert(ringbuf_is_empty());

}

#endif //_MSV_VER

// Support methods
char* concat_strings(const char* str1, const char* str2) {
    const size_t len1 = strlen(str1);
    const size_t len2 = strlen(str2) + 1; // includes terminating null

    char* const s = (char*)malloc(len1 + len2);
    memcpy(s, str1, len1);
    memcpy(s + len1, str2, len2);

    return s;
}<commit_msg>Elimination of a couple of warnings<commit_after>#include "group_shared.hpp"
#include <cassert>

#include <fcntl.h>
#include <sys/stat.h>
#include <sys/mman.h>

using namespace tightdb;

// Pre-declare local functions
char* concat_strings(const char* str1, const char* str2);

struct tightdb::ReadCount {
    uint32_t version;
    uint32_t count;
};

struct tightdb::SharedInfo {
    pthread_mutex_t readmutex;
    pthread_mutex_t writemutex;
    uint64_t filesize;
    uint32_t infosize;

    uint64_t current_top;
    uint32_t current_version;

    uint32_t capacity; // -1 so it can also be used as mask
    uint32_t put_pos;
    uint32_t get_pos;
    ReadCount readers[32]; // has to be power of two
};

// Does not work for windows yet
#ifndef _MSC_VER

SharedGroup::SharedGroup(const char* filename) : m_group(filename, false), m_info(NULL), m_isValid(false), m_version(-1)
{
    if (!m_group.is_valid()) return;

    // Open shared coordination buffer
    const char* const path = concat_strings(filename, ".lock");
    const int fd = open(path, O_RDWR|O_CREAT, S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH);
    free((void*)path);
    if (fd < 0) return;

    // Get size
    struct stat statbuf;
    if (fstat(fd, &statbuf) < 0) {
        close(fd);
        return;
    }
    size_t len = statbuf.st_size;

    // Handle empty files (first user)
    bool needInit = false;
    if (len == 0) {
        // Create new file
        len = sizeof(SharedInfo);
        int r = ftruncate(fd, len);
        static_cast<void>(r); // FIXME: We should probably check for error here!
        needInit = true;
    }

    // Map to memory
    void* const p = mmap(0, len, PROT_READ|PROT_WRITE, MAP_SHARED, fd, 0);
    close(fd); // no loner need file discriptor
    if (p == (void*)-1) return;

    m_info = (SharedInfo*)p;

    if (needInit) {
        pthread_mutex_lock(&m_info->readmutex);
        {
            const SlabAlloc& alloc = m_group.get_allocator();

            m_info->filesize = alloc.GetFileLen();
            m_info->infosize = (uint32_t)len;
            m_info->current_top = alloc.GetTopRef();;
            m_info->current_version = 0;
            m_info->capacity = 32-1;
            m_info->put_pos = 0;
            m_info->get_pos = 0;
        }
        pthread_mutex_unlock(&m_info->readmutex);
    }

    m_isValid = true;
}

SharedGroup::~SharedGroup()
{
    if (m_info) {
        munmap((void*)m_info, m_info->infosize);
    }
}

const Group& SharedGroup::start_read()
{
    size_t new_topref = 0;
    size_t new_filesize = 0;

    pthread_mutex_lock(&m_info->readmutex);
    {
        // Get the current top ref
        new_topref   = m_info->current_top;
        new_filesize = m_info->filesize;
        m_version    = m_info->current_version;

        // Update reader list
        if (ringbuf_is_empty()) {
            const ReadCount r2 = {m_info->current_version, 1};
            ringbuf_put(r2);
        }
        else {
            ReadCount& r = ringbuf_get_last();
            if (r.version == m_info->current_version)
                ++(r.count);
            else {
                const ReadCount r2 = {m_info->current_version, 1};
                ringbuf_put(r2);
            }
        }
    }
    pthread_mutex_unlock(&m_info->readmutex);

    // Make sure the group is up-to-date
    m_group.update_from_shared(new_topref, new_filesize);

    return m_group;
}

void SharedGroup::end_read()
{
    assert(m_version != (uint32_t)-1);

    pthread_mutex_lock(&m_info->readmutex);
    {
        // Find entry for current version
        const size_t ndx = ringbuf_find(m_version);
        assert(ndx != (size_t)-1);
        ReadCount& r = ringbuf_get(ndx);

        // Decrement count and remove as many entries as possible
        if (r.count == 1 && ringbuf_is_first(ndx)) {
            ringbuf_remove_first();
            while (!ringbuf_is_empty() && ringbuf_get_first().count == 0) {
                ringbuf_remove_first();
            }
        }
        else {
            assert(r.count > 0);
            --r.count;
        }
    }
    pthread_mutex_unlock(&m_info->readmutex);

    m_version = (uint32_t)-1;
}

Group& SharedGroup::start_write()
{
    // Get write lock
    // Note that this will not get released until we call
    // end_write().
    pthread_mutex_lock(&m_info->writemutex);

    // Get the current top ref
    const size_t new_topref   = m_info->current_top;
    const size_t new_filesize = m_info->filesize;

    // Make sure the group is up-to-date
    // zero ref means that the file has just been created
    if (new_topref != 0) {
        m_group.update_from_shared(new_topref, new_filesize);
    }

    return m_group;
}

void SharedGroup::end_write()
{
    m_group.commit();

    // Get the current top ref
    const SlabAlloc& alloc = m_group.get_allocator();
    const size_t new_topref   = alloc.GetTopRef();
    const size_t new_filesize = alloc.GetFileLen();

    // Update reader info
    pthread_mutex_lock(&m_info->readmutex);
    {
        m_info->current_top = new_topref;
        m_info->filesize    = new_filesize;
        ++m_info->current_version;
    }
    pthread_mutex_unlock(&m_info->readmutex);

    // Release write lock
    pthread_mutex_unlock(&m_info->writemutex);
}

bool SharedGroup::ringbuf_is_empty() const
{
    return (ringbuf_size() == 0);
}

size_t SharedGroup::ringbuf_size() const
{
    return ((m_info->put_pos - m_info->get_pos) & m_info->capacity);
}

size_t SharedGroup::ringbuf_capacity() const
{
    return m_info->capacity+1;
}

bool SharedGroup::ringbuf_is_first(size_t ndx) const {
    return (ndx == m_info->get_pos);
}

ReadCount& SharedGroup::ringbuf_get(size_t ndx)
{
    return m_info->readers[ndx];
}

ReadCount& SharedGroup::ringbuf_get_first()
{
    return m_info->readers[m_info->get_pos];
}

ReadCount& SharedGroup::ringbuf_get_last()
{
    const uint32_t lastPos = (m_info->put_pos - 1) & m_info->capacity;
    return m_info->readers[lastPos];
}

void SharedGroup::ringbuf_remove_first() {
    m_info->get_pos = (m_info->get_pos + 1) & m_info->capacity;
}

void SharedGroup::ringbuf_put(const ReadCount& v)
{
    const bool isFull = (ringbuf_size() == (m_info->capacity+1));

    if(isFull) {
        //TODO: expand buffer
        assert(false);
    }

    m_info->readers[m_info->put_pos] = v;
    m_info->put_pos = (m_info->put_pos + 1) & m_info->capacity;
}

size_t SharedGroup::ringbuf_find(uint32_t version) const
{
    uint32_t pos = m_info->get_pos;
    while (pos != m_info->put_pos) {
        const ReadCount& r = m_info->readers[pos];
        if (r.version == version)
            return pos;

        pos = (pos + 1) & m_info->capacity;
    }

    return (size_t)-1;
}

void SharedGroup::test_ringbuf()
{
    assert(ringbuf_is_empty());

    const ReadCount rc = {1, 1};
    ringbuf_put(rc);
    assert(ringbuf_size() == 1);

    ringbuf_remove_first();
    assert(ringbuf_is_empty());

    // Fill buffer
    const size_t capacity = ringbuf_capacity();
    for (size_t i = 0; i < capacity; ++i) {
        const ReadCount r = {1, (uint32_t)i};
        ringbuf_put(r);
        assert(ringbuf_get_last().count == i);
    }
    for (size_t i = 0; i < 32; ++i) {
        const ReadCount& r = ringbuf_get_first();
        assert(r.count == i);
        static_cast<void>(r);
        ringbuf_remove_first();
    }
    assert(ringbuf_is_empty());

}

#endif //_MSV_VER

// Support methods
char* concat_strings(const char* str1, const char* str2) {
    const size_t len1 = strlen(str1);
    const size_t len2 = strlen(str2) + 1; // includes terminating null

    char* const s = (char*)malloc(len1 + len2);
    memcpy(s, str1, len1);
    memcpy(s + len1, str2, len2);

    return s;
}
<|endoftext|>


<commit_before><commit_msg>Modified output of filters-test.cxx to CSV<commit_after><|endoftext|>