text: //
  // $Id$
  //
  
  //
  // Sphinx UDF function example
  //
  // Linux
  // gcc -fPIC -Wall -Wextra -shared -o udfexample.so udfexample.c
  // CREATE FUNCTION sequence RETURNS INT SONAME 'udfexample.so';
  // CREATE FUNCTION strtoint RETURNS INT SONAME 'udfexample.so';
  // CREATE FUNCTION avgmva RETURNS FLOAT SONAME 'udfexample.so';
  // CREATE FUNCTION failtest RETURNS STRING SONAME 'udfexample.so';
  //
  // Windows
  // cl /W4 /MTd /LD udfexample.c
  // CREATE FUNCTION sequence RETURNS INT SONAME 'udfexample.dll';
  // CREATE FUNCTION strtoint RETURNS INT SONAME 'udfexample.dll';
  // CREATE FUNCTION avgmva RETURNS FLOAT SONAME 'udfexample.dll';
  // CREATE FUNCTION failtest RETURNS STRING SONAME 'udfexample.dll';
  //
  
  #define _CRT_SECURE_NO_WARNINGS
  
  #include "sphinxudf.h"
  #include <stdio.h>
  #include <string.h>
  #include <stdlib.h>
  
  #ifdef _MSC_VER
  	#define snprintf	_snprintf
  	#define DLLEXPORT	__declspec(dllexport)
  #else
  	#define DLLEXPORT
  #endif
  
  #define UNUSED(x) (void)(x);
  
  /// UDF version control
  /// gets called once when the library is loaded
  DLLEXPORT int udfexample_ver()
  {
  	return SPH_UDF_VERSION;
  }
  
  //////////////////////////////////////////////////////////////////////////
  // EXAMPLE: SEQUENCE() FUNCTION, WITH A STATEFUL COUNTER
  //////////////////////////////////////////////////////////////////////////
  
  /// UDF initialization
  /// gets called on every query, when query begins
  /// args are filled with values for a particular query
  DLLEXPORT int sequence_init(SPH_UDF_INIT * init, SPH_UDF_ARGS * args, char * error_message)
  {
  	// check argument count
  	if (args->arg_count > 1)
  	{
  		snprintf(error_message, SPH_UDF_ERROR_LEN, "SEQUENCE() takes either 0 or 1 arguments");
  		return 1;
  	}
  
  	// check argument type
  	if (args->arg_count && args->arg_types[0] != SPH_UDF_TYPE_UINT32)
  	{
  		snprintf(error_message, SPH_UDF_ERROR_LEN, "SEQUENCE() requires 1st argument to be uint");
  		return 1;
  	}
  
  	// allocate and init counter storage
  	init->func_data = (void *)malloc(sizeof(int));
  	if (!init->func_data)
  	{
  		snprintf(error_message, SPH_UDF_ERROR_LEN, "malloc() failed");
  		return 1;
  	}
  	*(int *)init->func_data = 1;
  
  	// all done
  	return 0;
  }
  
  
  /// UDF deinitialization
  /// gets called on every query, when query ends
  DLLEXPORT void sequence_deinit(SPH_UDF_INIT * init)
  {
  	// deallocate storage
  	if (init->func_data)
  	{
  		free(init->func_data);
  		init->func_data = NULL;
  	}
  }
  
  
  /// UDF implementation
  /// gets called for every row, unless optimized away
  DLLEXPORT sphinx_int64_t sequence(SPH_UDF_INIT * init, SPH_UDF_ARGS * args, char * error_message)
  {
  	UNUSED(error_message);
  	int res = (*(int *)init->func_data)++;
  	if (args->arg_count)
  		res += *(int *)args->arg_values[0];
  	return res;
  }
  
  //////////////////////////////////////////////////////////////////////////
  // EXAMPLE: STRTOINT() FUNCTION, SIMPLE AND STATELESS
  //////////////////////////////////////////////////////////////////////////
  
  DLLEXPORT int strtoint_init(SPH_UDF_INIT * init, SPH_UDF_ARGS * args, char * error_message)
  {
  	UNUSED(init);
  	if (args->arg_count != 1 || args->arg_types[0] != SPH_UDF_TYPE_STRING)
  	{
  		snprintf(error_message, SPH_UDF_ERROR_LEN, "STRTOINT() requires 1 string argument");
  		return 1;
  	}
  	return 0;
  }
  
  
  DLLEXPORT sphinx_int64_t strtoint(SPH_UDF_INIT * init, SPH_UDF_ARGS * args, char * error_message)
  {
  	UNUSED(init);
  	UNUSED(error_message);
  	const char * s = args->arg_values[0];
  	int len = args->str_lengths[0], res = 0;
  
  	while (len > 0 && *s >= '0' && *s <= '9')
  	{
  		res += *s - '0';
  		len--;
  	}
  
  	return res;
  }
  
  //////////////////////////////////////////////////////////////////////////
  // EXAMPLE: AVGMVA() FUNCTION, POLYMORPHIC, AND WITH MVA TYPES
  //////////////////////////////////////////////////////////////////////////
  
  DLLEXPORT int avgmva_init(SPH_UDF_INIT * init, SPH_UDF_ARGS * args, char * error_message)
  {
  	if (args->arg_count != 1 ||
  		(args->arg_types[0] != SPH_UDF_TYPE_UINT32SET && args->arg_types[0] != SPH_UDF_TYPE_INT64SET))
  	{
  		snprintf(error_message, SPH_UDF_ERROR_LEN, "AVGMVA() requires 1 MVA argument");
  		return 1;
  	}
  
  	// store our mva vs mva64 flag to func_data
  	init->func_data = (void *)(long long int)(args->arg_types[0] == SPH_UDF_TYPE_INT64SET ? 1 : 0);
  	return 0;
  }
  
  
  DLLEXPORT double avgmva(SPH_UDF_INIT * init, SPH_UDF_ARGS * args, char * error_message)
  {
  	UNUSED(error_message);
  	double res = 0;
  	int i, is64;
  	int len = args->str_lengths[0]; // since 3.0, element count is passed here
  
  	if (!args->arg_values[0])
  		return res;
  
  	is64 = (long long int)(init->func_data) != 0;
  	if (is64)
  	{
  		// handle mva64
  		sphinx_uint64_t * mva = (sphinx_uint64_t *)args->arg_values[0];
  		for (i = 0; i < len; i++)
  			res += *mva++; // NOTE: unaligned load
  	} else
  	{
  		// handle mva32
  		unsigned int * mva = (unsigned int *)args->arg_values[0];
  		for (i = 0; i < len; i++)
  			res += *mva++; // NOTE: unaligned load
  	}
  
  	return res / len;
  }
  
  //////////////////////////////////////////////////////////////////////////
  // EXAMPLE: FAILTEST() FUNCTION, STOPS PROCESSING AFTER 3 ROWS
  //////////////////////////////////////////////////////////////////////////
  
  DLLEXPORT int failtest_init(SPH_UDF_INIT * init, SPH_UDF_ARGS * args, char * error_message)
  {
  	if (args->arg_count != 0)
  	{
  		snprintf(error_message, SPH_UDF_ERROR_LEN, "FAILTEST() takes 0 arguments");
  		return 1;
  	}
  
  	// allocate counter storage, init counter
  	init->func_data = (void *)malloc(sizeof(int));
  	if (!init->func_data)
  	{
  		snprintf(error_message, SPH_UDF_ERROR_LEN, "malloc() failed");
  		return 1;
  	}
  	*(int *)init->func_data = 1;
  
  	return 0;
  }
  
  
  DLLEXPORT char * failtest(SPH_UDF_INIT * init, SPH_UDF_ARGS * args, char * error_message)
  {
  	int a = (*(int *)init->func_data)++;
  
  	if (a < 4)
  	{
  		// IMPORTANT!
  		// note that strings we return to Sphinx MUST be allocated using fn_malloc!
  		char * res = args->fn_malloc(16);
  		snprintf(res, 16, "val%d", a);
  		return res;
  	}
  
  	if (a == 4)
  	{
  		// starting with UDF version 12, we can emit an error message from the main
  		// UDF function too, and not just a 1-byte flag
  		snprintf(error_message, SPH_UDF_ERROR_LEN, "err%d", a); // expected error
  		return NULL;
  	}
  
  	// this must never get called
  	// because the engine guarantees to stop calling UDF once we reported an error
  	// however, to test that the engine does, lets test
  	char * res = args->fn_malloc(16);
  	snprintf(res, 16, "wtf%d", a);
  	return res;
  }
  
  
  DLLEXPORT void failtest_deinit(SPH_UDF_INIT * init)
  {
  	// deallocate storage
  	if (init->func_data)
  	{
  		free(init->func_data);
  		init->func_data = NULL;
  	}
  }
  
  //////////////////////////////////////////////////////////////////////////
  // EXAMPLE: BATCHTEST()
  //////////////////////////////////////////////////////////////////////////
  
  DLLEXPORT int batchtest_init(SPH_UDF_INIT * init, SPH_UDF_ARGS * args, char * error_message)
  {
  	UNUSED(init);
  	if (args->arg_count != 1 || args->arg_types[0] != SPH_UDF_TYPE_FLOAT_VEC)
  	{
  		snprintf(error_message, SPH_UDF_ERROR_LEN, "BATCHTEST() requires exactly one FLOAT_VEC argument");
  		return 1;
  	}
  
  	return 0;
  }
  
  
  DLLEXPORT void batchtest_batch(SPH_UDF_INIT * init, SPH_UDF_ARGS * args, float * results, int batch_size, char * error_message)
  {
  	UNUSED(init);
  	UNUSED(error_message);
  
  	for (int i = 0; i < batch_size; i++)
  	{
  		float * fvec = (float *)args->arg_values[i];
  		int len = args->str_lengths[i];
  
  		float res = 0.0f;
  		for (int j = 0; j < len; j++)
  			res += fvec[j];
  
  		results[i] = res;
  	}
  }
  
  // FIXME! add a ranker plugin example?
  
  //
  // $Id$
  //
  