/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
/*
 *  Authors: Jeffrey Stedfast <fejj@ximian.com>
 *
 *  Copyright 2001 Ximian, Inc. (www.ximian.com)
 *
 *  This program is free software; you can redistribute it and/or modify
 *  it under the terms of the GNU General Public License as published by
 *  the Free Software Foundation; either version 2 of the License, or
 *  (at your option) any later version.
 *
 *  This program is distributed in the hope that it will be useful,
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *  GNU General Public License for more details.
 *
 *  You should have received a copy of the GNU General Public License
 *  along with this program; if not, write to the Free Software
 *  Foundation, Inc., 59 Temple Street #330, Boston, MA 02111-1307, USA.
 *
 */


#ifdef HAVE_CONFIG_H
#include <config.h>
#endif

#include <string.h> /* for memcpy */
#include "gmime-filter.h"


struct _GMimeFilterPrivate {
	char *inbuf;
	size_t inlen;
};

#define PRE_HEAD (64)
#define BACK_HEAD (64)
#define _PRIVATE(o) (((GMimeFilter *)(o))->priv)



/**
 * g_mime_filter_construct:
 * @filter: filter
 * @filter_template: filter template
 *
 * Initializes a filter object using the virtual methods in @filter_template.
 **/
void
g_mime_filter_construct (GMimeFilter *filter, GMimeFilter *filter_template)
{
	g_return_if_fail (filter != NULL);
	g_return_if_fail (filter_template != NULL);
	
	filter->priv = g_new0 (struct _GMimeFilterPrivate, 1);
	filter->outptr = NULL;
	filter->outreal = NULL;
	filter->outbuf = NULL;
	filter->outsize = 0;
	
	filter->backbuf = NULL;
	filter->backsize = 0;
	filter->backlen = 0;
	
	filter->destroy = filter_template->destroy;
	filter->copy = filter_template->copy;
	filter->filter = filter_template->filter;
	filter->complete = filter_template->complete;
	filter->reset = filter_template->reset;
}


/**
 * g_mime_filter_destroy:
 * @filter: filter
 *
 * Destroys @filter and releases the memory to the system.
 **/
void
g_mime_filter_destroy (GMimeFilter *filter)
{
	if (filter) {
		g_free (filter->priv->inbuf);
		g_free (filter->priv);
		g_free (filter->outreal);
		g_free (filter->backbuf);
		filter->destroy (filter);
	}
}


/**
 * g_mime_filter_copy:
 * @filter: filter
 *
 * Copies @filter into a new GMimeFilter object.
 *
 * Returns a duplicate of @filter.
 **/
GMimeFilter *
g_mime_filter_copy (GMimeFilter *filter)
{
	return filter->copy (filter);
}


static void
filter_run (GMimeFilter *filter, char *in, size_t len, size_t prespace,
	    char **out, size_t *outlen, size_t *outprespace,
	    void (*filterfunc) (GMimeFilter *filter,
				char *in, size_t len, size_t prespace,
				char **out, size_t *outlen, size_t *outprespace))
{
	/*
	  here we take a performance hit, if the input buffer doesn't
	  have the pre-space required.  We make a buffer that does ...
	*/
	if (prespace < filter->backlen) {
		struct _GMimeFilterPrivate *p = _PRIVATE (filter);
		size_t newlen = len + prespace + filter->backlen;
		
		if (p->inlen < newlen) {
			/* NOTE: g_realloc copies data, we dont need that (slower) */
			g_free (p->inbuf);
			p->inbuf = g_malloc (newlen + PRE_HEAD);
			p->inlen = newlen + PRE_HEAD;
		}
		
		/* copy to end of structure */
		memcpy (p->inbuf + p->inlen - len, in, len);
		in = p->inbuf + p->inlen - len;
		prespace = p->inlen - len;
	}
	
	/* preload any backed up data */
	if (filter->backlen > 0) {
		memcpy (in - filter->backlen, filter->backbuf, filter->backlen);
		in -= filter->backlen;
		len += filter->backlen;
		prespace -= filter->backlen;
		filter->backlen = 0;
	}
	
	filterfunc (filter, in, len, prespace, out, outlen, outprespace);
}


/**
 * g_mime_filter_filter:
 * @filter: filter
 * @in: input buffer
 * @len: input buffer length
 * @prespace: prespace buffer length
 * @out: pointer to output buffer
 * @outlen: pointer to output length
 * @outprespace: pointer to output prespace buffer length
 *
 * Filters the input data and writes it to @out.
 **/
void
g_mime_filter_filter (GMimeFilter *filter,
		      char *in, size_t len, size_t prespace,
		      char **out, size_t *outlen, size_t *outprespace)
{
	g_return_if_fail (filter != NULL);
	
	filter_run (filter, in, len, prespace, out, outlen, outprespace, filter->filter);
}


/**
 * g_mime_filter_complete:
 * @filter: filter
 * @in: input buffer
 * @len: input length
 * @prespace: prespace buffer length
 * @out: pointer to output buffer
 * @outlen: pointer to output length
 * @outprespace: pointer to output prespace buffer length
 *
 * Completes the filtering.
 **/
void
g_mime_filter_complete (GMimeFilter *filter,
			char *in, size_t len, size_t prespace,
			char **out, size_t *outlen, size_t *outprespace)
{
	g_return_if_fail (filter != NULL);
	
	filter_run (filter, in, len, prespace, out, outlen, outprespace, filter->complete);
}


/**
 * g_mime_filter_reset:
 * @filter:
 *
 * Resets the filter.
 **/
void
g_mime_filter_reset (GMimeFilter *filter)
{
	g_return_if_fail (filter != NULL);
	
	filter->reset (filter);
	
	/* could free some buffers, if they are really big? */
	filter->backlen = 0;
}


/**
 * g_mime_filter_backup:
 * @filter: filter
 * @data: 
 * @length: 
 *
 * Sets number of bytes backed up on the input, new calls replace
 * previous ones
 **/
void
g_mime_filter_backup (GMimeFilter *filter, const char *data, size_t length)
{
	g_return_if_fail (filter != NULL);
	
	if (filter->backsize < length) {
		/* g_realloc copies data, unnecessary overhead */
		g_free (filter->backbuf);
		filter->backbuf = g_malloc (length + BACK_HEAD);
		filter->backsize = length + BACK_HEAD;
	}
	
	filter->backlen = length;
	memcpy (filter->backbuf, data, length);
}


/**
 * g_mime_filter_set_size:
 * @filter: filter
 * @size: 
 * @keep: 
 *
 * Ensure this much size available for filter output (if required)
 **/
void
g_mime_filter_set_size (GMimeFilter *filter, size_t size, gboolean keep)
{
	g_return_if_fail (filter != NULL);
	
	if (filter->outsize < size) {
		size_t offset = filter->outptr - filter->outreal;
		
		if (keep) {
			filter->outreal = g_realloc (filter->outreal, size + PRE_HEAD * 4);
		} else {
			g_free (filter->outreal);
			filter->outreal = g_malloc (size + PRE_HEAD * 4);
		}
		
		filter->outptr = filter->outreal + offset;
		filter->outbuf = filter->outreal + PRE_HEAD * 4;
		filter->outsize = size;
		
		/* this could be offset from the end of the structure, but 
		   this should be good enough */
		
		filter->outpre = PRE_HEAD * 4;
	}
}


syntax highlighted by Code2HTML, v. 0.9.1