/*

build this into a Postgres shared library, then

create or replace function drive_conversion(count int, enc1 name, enc2 name, input bytea) returns void
strict volatile language c as '$libdir/drive_conversion.so';

select drive_conversion(1000, 'some-text', 'enc1', 'enc2');

 */

#include "postgres.h"

#include "fmgr.h"
#include "miscadmin.h"
#include "mb/pg_wchar.h"
#include "catalog/namespace.h"

#include "utils/builtins.h"

PG_MODULE_MAGIC;

/*
 * drive_conversion(count int, enc1 name, enc2 name, input text) returns void
 */
PG_FUNCTION_INFO_V1(drive_conversion);
Datum
drive_conversion(PG_FUNCTION_ARGS)
{

	int			count = PG_GETARG_INT32(0);
	char	   *src_encoding_name = NameStr(*PG_GETARG_NAME(1));
	int			src_encoding = pg_char_to_encoding(src_encoding_name);
	char	   *dest_encoding_name = NameStr(*PG_GETARG_NAME(2));
	int			dest_encoding = pg_char_to_encoding(dest_encoding_name);
	bytea	   *string = PG_GETARG_BYTEA_PP(3);
	const char *src_str;
	char	   *dest_str;
	bytea	   *retval;
	int			len;

	if (src_encoding < 0)
		ereport(ERROR,
				(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
				 errmsg("invalid source encoding name \"%s\"",
						src_encoding_name)));
	if (dest_encoding < 0)
		ereport(ERROR,
				(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
				 errmsg("invalid destination encoding name \"%s\"",
						dest_encoding_name)));

	/* make sure that source string is valid */
	len = VARSIZE_ANY_EXHDR(string);
	src_str = VARDATA_ANY(string);
	pg_verify_mbstr_len(src_encoding, src_str, len, false);

	/* perform conversion for the specified repetitions */
	while (count-- > 0)
	{
		dest_str = (char *) pg_do_encoding_conversion((unsigned char *) unconstify(char *, src_str),
													  len,
													  src_encoding,
													  dest_encoding);
	}

	/* update len if conversion actually happened */
	if (dest_str != src_str)
		len = strlen(dest_str);

	/*
	 * build bytea data type structure.
	 */
	retval = (bytea *) palloc(len + VARHDRSZ);
	SET_VARSIZE(retval, len + VARHDRSZ);
	memcpy(VARDATA(retval), dest_str, len);

	if (dest_str != src_str)
		pfree(dest_str);

	/* free memory if allocated by the toaster */
	PG_FREE_IF_COPY(string, 0);

	PG_RETURN_VOID();
}
